use crate::{
    dependencies::{
        get_dependencies,
        get_mock_library,
        get_standard_library,
        is_root_arcis,
        libraries_to_dependency_graph,
        Libraries,
    },
    interpreter::{Interpreter, ParsedMacroStore},
    macro_parser::MacroParser,
};
use syn::visit::Visit;

pub struct InterpreterBuilder<'ast> {
    module: &'ast syn::ItemMod,
    dependencies: Libraries,
    standard_library: std::borrow::Cow<'ast, [syn::File]>,
    mock_library: std::borrow::Cow<'ast, [syn::File]>,
    macro_parser: MacroParser,
}

impl<'ast> InterpreterBuilder<'ast> {
    pub fn new(module: &'ast syn::ItemMod) -> Self {
        let dependencies = get_dependencies();
        let standard_library = get_standard_library();
        let mock_library = get_mock_library();
        let mut macro_parser = MacroParser::default();
        macro_parser.visit_all(module, &mock_library, &standard_library, &dependencies);
        Self {
            module,
            dependencies,
            standard_library,
            mock_library,
            macro_parser,
        }
    }
    pub fn run<'a, 'b>(&'b self) -> Interpreter<'a>
    where
        'ast: 'a,
        'b: 'a,
    {
        let parsed_macro_store = self.macro_parser.finish();
        build_interpreter(
            self.module,
            parsed_macro_store,
            &self.mock_library,
            &self.standard_library,
            &self.dependencies,
        )
    }
}

fn build_interpreter<'ast>(
    module: &'ast syn::ItemMod,
    parsed_macros: ParsedMacroStore<'ast>,
    mock_library: &'ast [syn::File],
    standard_library: &'ast [syn::File],
    dependencies: &'ast Libraries,
) -> Interpreter<'ast> {
    let mut interpreter = Interpreter::with_standard_library(
        parsed_macros,
        libraries_to_dependency_graph(dependencies),
    );
    syn::visit::visit_item_mod(&mut interpreter, module);
    for file in dependencies.file_iter() {
        interpreter.visit_file(file);
    }
    let is_arcis = is_root_arcis();
    for file in standard_library.iter() {
        for std_module in file.items.iter().filter_map(|item| {
            if let syn::Item::Mod(item_mod) = item {
                if (!is_arcis || item_mod.content != module.content)
                    && item_mod.attrs.iter().any(|attr| {
                        attr.meta
                            .path()
                            .get_ident()
                            .is_some_and(|i| i == "encrypted_library")
                    })
                {
                    return Some(item_mod);
                }
            }
            None
        }) {
            syn::visit::visit_item_mod(&mut interpreter, std_module);
        }
    }
    for file in mock_library.iter() {
        syn::visit::visit_file(&mut interpreter, file);
    }
    interpreter.handle_all_unhandled_items();
    interpreter
}
