use super::super::visitor::FnVisitor;
use super::ApiDependencyGraph;
use super::Config;
use super::dep_edge::DepEdge;
use super::dep_node::{DepNode, desc_str};
use super::transform::TransformKind;
use super::ty_wrapper::TyWrapper;
use super::utils;
use crate::analysis::utils::def_path::path_str_def_id;
use crate::rap_debug;
use crate::rap_trace;
use crate::utils::fs::rap_create_file;
use petgraph::Direction;
use petgraph::visit::EdgeRef;
use rustc_middle::ty::{self, Ty, TyCtxt};
use std::collections::HashSet;

impl<'tcx> ApiDependencyGraph<'tcx> {
    pub fn eligible_nodes_with(&self, tys: &[Ty<'tcx>]) -> Vec<DepNode<'tcx>> {
        let check_ty = |ty: Ty<'tcx>| {
            tys.iter()
                .any(|avail_ty| utils::is_ty_eq(*avail_ty, ty, self.tcx))
        };

        self.graph
            .node_indices()
            .filter_map(|index| match self.graph[index] {
                DepNode::Api(fn_did, args)
                    if self
                        .graph
                        .neighbors_directed(index, Direction::Incoming)
                        .all(|neighbor| {
                            let ty = self.graph[neighbor].expect_ty().ty();
                            utils::is_fuzzable_ty(ty, self.tcx) || check_ty(ty)
                        }) =>
                {
                    Some(self.graph[index])
                }
                DepNode::Ty(ty)
                    if self
                        .graph
                        .neighbors_directed(index, Direction::Incoming)
                        .any(|neighbor| {
                            if let Some(ty) = self.graph[neighbor].as_ty() {
                                check_ty(ty.ty())
                            } else {
                                false
                            }
                        }) =>
                {
                    Some(self.graph[index])
                }
                _ => None,
            })
            .collect()
    }

    pub fn eligible_transforms_to(&self, ty: Ty<'tcx>) -> Vec<(TyWrapper<'tcx>, TransformKind)> {
        let mut set = HashSet::new();
        if let Some(node) = self.get_index(DepNode::Ty(ty.into())) {
            for edge in self.graph.edges_directed(node, Direction::Incoming) {
                if let Some(kind) = edge.weight().as_transform_kind() {
                    let source_ty = self.graph[edge.source()].expect_ty();
                    set.insert((source_ty, kind));
                }
            }
        }
        set.into_iter().collect()
    }
}
