mod common;

use brokk_bifrost::{IAnalyzer, ImportAnalysisProvider, ProjectFile, PythonAnalyzer};
use std::collections::BTreeSet;

use common::InlineTestProject;

fn inline_project(files: &[(&str, &str)]) -> common::BuiltInlineTestProject {
    files
        .iter()
        .fold(
            InlineTestProject::with_language(brokk_bifrost::Language::Python),
            |project, (path, contents)| project.file(*path, *contents),
        )
        .build()
}

#[test]
fn module_code_unit_created_with_top_level_children_only() {
    let project = inline_project(&[(
        "mod.py",
        r#"
        class A:
            class Inner:
                pass
        def f():
            pass
        x = 1
        "#,
    )]);
    let analyzer = PythonAnalyzer::from_project(project.project().clone());

    let module = analyzer
        .get_definitions("mod")
        .into_iter()
        .find(|code_unit| code_unit.is_module())
        .unwrap();
    let child_fqns: Vec<_> = analyzer
        .get_direct_children(&module)
        .into_iter()
        .map(|code_unit| code_unit.fq_name())
        .collect();

    assert_eq!(vec!["mod.A", "mod.f", "mod.x"], child_fqns);
}

#[test]
fn module_code_unit_created_for_init_py_package_name() {
    let project = inline_project(&[(
        "pkg/__init__.py",
        r#"
        class A:
            pass
        def f():
            pass
        "#,
    )]);
    let analyzer = PythonAnalyzer::from_project(project.project().clone());

    let module = analyzer
        .get_definitions("pkg")
        .into_iter()
        .find(|code_unit| code_unit.is_module())
        .unwrap();
    let child_fqns: Vec<_> = analyzer
        .get_direct_children(&module)
        .into_iter()
        .map(|code_unit| code_unit.fq_name())
        .collect();

    assert_eq!(vec!["pkg.A", "pkg.f"], child_fqns);
}

#[test]
fn module_code_units_are_per_file_in_packaged_directory() {
    let project = inline_project(&[
        (
            "pkg/a.py",
            r#"
            class A:
                pass
            "#,
        ),
        (
            "pkg/b.py",
            r#"
            def f():
                pass
            "#,
        ),
    ]);
    let analyzer = PythonAnalyzer::from_project(project.project().clone());

    let mod_a = analyzer
        .get_definitions("pkg.a")
        .into_iter()
        .find(|code_unit| code_unit.is_module())
        .unwrap();
    let mod_b = analyzer
        .get_definitions("pkg.b")
        .into_iter()
        .find(|code_unit| code_unit.is_module())
        .unwrap();

    let children_a: Vec<_> = analyzer
        .get_direct_children(&mod_a)
        .into_iter()
        .map(|code_unit| code_unit.fq_name())
        .collect();
    let children_b: Vec<_> = analyzer
        .get_direct_children(&mod_b)
        .into_iter()
        .map(|code_unit| code_unit.fq_name())
        .collect();

    assert_eq!(vec!["pkg.a.A"], children_a);
    assert_eq!(vec!["pkg.b.f"], children_b);
}

#[test]
fn module_code_units_use_python_src_layout_import_root() {
    let project = inline_project(&[
        ("src/pkg/__init__.py", ""),
        (
            "src/pkg/mod.py",
            r#"
            class Thing:
                pass
            "#,
        ),
    ]);
    let analyzer = PythonAnalyzer::from_project(project.project().clone());

    let module = analyzer
        .get_definitions("pkg.mod")
        .into_iter()
        .find(|code_unit| code_unit.is_module())
        .unwrap();
    let children: Vec<_> = analyzer
        .get_direct_children(&module)
        .into_iter()
        .map(|code_unit| code_unit.fq_name())
        .collect();

    assert_eq!(vec!["pkg.mod.Thing"], children);
}

#[test]
fn referencing_files_resolve_python_src_layout_modules() {
    let project = inline_project(&[
        ("src/pkg/__init__.py", ""),
        (
            "src/pkg/mod.py",
            r#"
            VALUE = 1
            "#,
        ),
        (
            "src/pkg/consumer.py",
            r#"
            from pkg import mod
            "#,
        ),
    ]);
    let root = project.root().to_path_buf();
    let analyzer = PythonAnalyzer::from_project(project.project().clone());
    let mod_file = ProjectFile::new(root.clone(), "src/pkg/mod.py");
    let consumer_file = ProjectFile::new(root, "src/pkg/consumer.py");

    let referencing: BTreeSet<_> = analyzer
        .referencing_files_of(&mod_file)
        .into_iter()
        .collect();

    assert_eq!(BTreeSet::from([consumer_file]), referencing);
}
