//! Integration tests for the new language behavior system
//!
//! These tests verify that the new resolution and inheritance methods
//! correctly capture language-specific semantics without relying on
//! SimpleIndexer integration (which hasn't been updated yet).

use codanna::parsing::{
    LanguageBehavior, PhpBehavior, PythonBehavior, RustBehavior, ScopeLevel, ScopeType,
    TypeScriptBehavior,
};
use codanna::{FileId, SymbolId};

/// Test TypeScript resolution behavior
#[test]
fn test_typescript_resolution_behavior() {
    println!("\n=== Testing TypeScript Resolution Behavior ===");

    let behavior = TypeScriptBehavior::new();
    let file_id = FileId::new(1).unwrap();
    let mut ctx = behavior.create_resolution_context(file_id);

    // Simulate TypeScript hoisting and scoping
    println!("Testing TypeScript hoisting and namespaces...");

    // Add symbols at different scope levels
    ctx.add_symbol(
        "globalVar".to_string(),
        SymbolId::new(1).unwrap(),
        ScopeLevel::Global,
    );
    ctx.add_symbol(
        "moduleFunc".to_string(),
        SymbolId::new(2).unwrap(),
        ScopeLevel::Module,
    );
    ctx.add_symbol(
        "localLet".to_string(),
        SymbolId::new(3).unwrap(),
        ScopeLevel::Local,
    );

    // Enter a function scope (TypeScript hoisting should apply)
    ctx.enter_scope(ScopeType::function());
    ctx.add_symbol(
        "functionParam".to_string(),
        SymbolId::new(4).unwrap(),
        ScopeLevel::Local,
    );

    // Test resolution
    assert_eq!(ctx.resolve("globalVar"), Some(SymbolId::new(1).unwrap()));
    assert_eq!(ctx.resolve("moduleFunc"), Some(SymbolId::new(2).unwrap()));
    assert_eq!(ctx.resolve("localLet"), Some(SymbolId::new(3).unwrap()));
    assert_eq!(
        ctx.resolve("functionParam"),
        Some(SymbolId::new(4).unwrap())
    );

    // Exit function scope
    ctx.exit_scope();

    // Function param should no longer be accessible in TypeScript
    assert_eq!(ctx.resolve("functionParam"), None); // TypeScript clears local scope when exiting function

    println!("✓ TypeScript scoping works");

    // Test namespace resolution
    ctx.enter_scope(ScopeType::Namespace);
    ctx.add_symbol(
        "NamespaceClass".to_string(),
        SymbolId::new(5).unwrap(),
        ScopeLevel::Module,
    );

    let symbols = ctx.symbols_in_scope();
    println!("Symbols in scope after namespace:");
    for (name, id, level) in &symbols {
        println!("  {name} (id: {id:?}) at level: {level:?}");
    }

    assert!(symbols.iter().any(|(n, _, _)| n == "NamespaceClass"));
    println!("✓ TypeScript namespace handling works");
}

/// Test TypeScript inheritance behavior (interfaces and extends)
#[test]
fn test_typescript_inheritance_behavior() {
    println!("\n=== Testing TypeScript Inheritance Behavior ===");

    let behavior = TypeScriptBehavior::new();
    let mut resolver = behavior.create_inheritance_resolver();

    // Set up TypeScript class/interface hierarchy
    // interface Vehicle { drive(): void }
    // interface Electric { charge(): void }
    // class Car implements Vehicle { drive() {} }
    // class Tesla extends Car implements Electric { charge() {} }

    println!("Setting up TypeScript inheritance hierarchy...");

    // Add interface methods
    resolver.add_type_methods("Vehicle".to_string(), vec!["drive".to_string()]);
    resolver.add_type_methods("Electric".to_string(), vec!["charge".to_string()]);

    // Add class methods
    resolver.add_type_methods(
        "Car".to_string(),
        vec!["drive".to_string(), "honk".to_string()],
    );
    resolver.add_type_methods(
        "Tesla".to_string(),
        vec!["charge".to_string(), "autopilot".to_string()],
    );

    // Add relationships
    resolver.add_inheritance("Car".to_string(), "Vehicle".to_string(), "implements");
    resolver.add_inheritance("Tesla".to_string(), "Car".to_string(), "extends");
    resolver.add_inheritance("Tesla".to_string(), "Electric".to_string(), "implements");

    // Test method resolution
    println!("Testing method resolution...");
    assert_eq!(
        resolver.resolve_method("Tesla", "autopilot"),
        Some("Tesla".to_string())
    );
    assert_eq!(
        resolver.resolve_method("Tesla", "drive"),
        Some("Car".to_string())
    );
    assert_eq!(
        resolver.resolve_method("Tesla", "charge"),
        Some("Tesla".to_string())
    );

    // Test inheritance chain
    let chain = resolver.get_inheritance_chain("Tesla");
    println!("Tesla inheritance chain: {chain:?}");
    assert!(chain.contains(&"Tesla".to_string()));
    assert!(chain.contains(&"Car".to_string()));
    assert!(chain.contains(&"Vehicle".to_string()));
    assert!(chain.contains(&"Electric".to_string()));

    // Test subtype relationships
    assert!(resolver.is_subtype("Tesla", "Car"));
    assert!(resolver.is_subtype("Tesla", "Vehicle"));
    assert!(resolver.is_subtype("Tesla", "Electric"));
    assert!(resolver.is_subtype("Car", "Vehicle"));
    assert!(!resolver.is_subtype("Vehicle", "Car"));

    println!("✓ TypeScript inheritance resolution works correctly");

    // Test relationship mapping
    assert_eq!(
        behavior.map_relationship("extends"),
        codanna::relationship::RelationKind::Extends
    );
    assert_eq!(
        behavior.map_relationship("implements"),
        codanna::relationship::RelationKind::Implements
    );
    println!("✓ TypeScript relationship mapping works");
}

/// Test Python LEGB resolution behavior
#[test]
fn test_python_legb_resolution() {
    println!("\n=== Testing Python LEGB Resolution ===");

    let behavior = PythonBehavior::new();
    let file_id = FileId::new(1).unwrap();
    let mut ctx = behavior.create_resolution_context(file_id);

    // Simulate Python LEGB scope (Local, Enclosing, Global, Built-in)
    println!("Setting up Python LEGB scopes...");

    // Built-in (Global level in our model)
    ctx.add_symbol(
        "print".to_string(),
        SymbolId::new(100).unwrap(),
        ScopeLevel::Global,
    );
    ctx.add_symbol(
        "len".to_string(),
        SymbolId::new(101).unwrap(),
        ScopeLevel::Global,
    );

    // Global scope
    ctx.add_symbol(
        "global_var".to_string(),
        SymbolId::new(1).unwrap(),
        ScopeLevel::Module,
    );

    // Enter outer function (Enclosing scope)
    ctx.enter_scope(ScopeType::function());
    ctx.add_symbol(
        "outer_var".to_string(),
        SymbolId::new(2).unwrap(),
        ScopeLevel::Local,
    );

    // Enter inner function (Local scope)
    ctx.enter_scope(ScopeType::function());
    ctx.add_symbol(
        "local_var".to_string(),
        SymbolId::new(3).unwrap(),
        ScopeLevel::Local,
    );

    // Test LEGB resolution order
    println!("Testing LEGB resolution order...");
    assert_eq!(ctx.resolve("local_var"), Some(SymbolId::new(3).unwrap())); // L
    assert_eq!(ctx.resolve("outer_var"), Some(SymbolId::new(2).unwrap())); // E (simulated)
    assert_eq!(ctx.resolve("global_var"), Some(SymbolId::new(1).unwrap())); // G
    assert_eq!(ctx.resolve("print"), Some(SymbolId::new(100).unwrap())); // B

    let symbols = ctx.symbols_in_scope();
    println!("All symbols in Python scope:");
    for (name, id, level) in &symbols {
        println!("  {name} (id: {id:?}) at level: {level:?}");
    }

    println!("✓ Python LEGB resolution works");
}

/// Test Python multiple inheritance with MRO
#[test]
fn test_python_mro_inheritance() {
    println!("\n=== Testing Python MRO (Method Resolution Order) ===");

    let behavior = PythonBehavior::new();
    let mut resolver = behavior.create_inheritance_resolver();

    // Classic diamond problem in Python
    // class A: method_a()
    // class B(A): method_b()
    // class C(A): method_c()
    // class D(B, C): method_d()

    println!("Setting up Python diamond inheritance...");

    resolver.add_type_methods("A".to_string(), vec!["method_a".to_string()]);
    resolver.add_type_methods("B".to_string(), vec!["method_b".to_string()]);
    resolver.add_type_methods("C".to_string(), vec!["method_c".to_string()]);
    resolver.add_type_methods("D".to_string(), vec!["method_d".to_string()]);

    resolver.add_inheritance("B".to_string(), "A".to_string(), "inherits");
    resolver.add_inheritance("C".to_string(), "A".to_string(), "inherits");
    resolver.add_inheritance("D".to_string(), "B".to_string(), "inherits");
    resolver.add_inheritance("D".to_string(), "C".to_string(), "inherits");

    // Test MRO
    let chain = resolver.get_inheritance_chain("D");
    println!("Python MRO for D: {chain:?}");

    // D should have access to all methods
    let all_methods = resolver.get_all_methods("D");
    println!("All methods available on D: {all_methods:?}");
    assert!(all_methods.contains(&"method_a".to_string()));
    assert!(all_methods.contains(&"method_b".to_string()));
    assert!(all_methods.contains(&"method_c".to_string()));
    assert!(all_methods.contains(&"method_d".to_string()));

    println!("✓ Python MRO works correctly");
}

/// Test Rust trait resolution behavior
#[test]
fn test_rust_trait_resolution() {
    println!("\n=== Testing Rust Trait Resolution ===");

    let behavior = RustBehavior::new();

    // Test Rust-specific behavior flags first
    assert!(behavior.supports_traits());
    assert!(behavior.supports_inherent_methods());
    assert_eq!(behavior.module_separator(), "::");
    println!("✓ Rust-specific behavior flags correct");

    // For Rust trait resolution, we need to use the concrete type
    // because Rust requires explicit distinction between traits and types
    // This is tested separately in test_rust_trait_resolution.rs
    println!("✓ Rust trait resolution tested in dedicated test file");
}

/// Test PHP namespace and trait resolution
#[test]
fn test_php_namespace_resolution() {
    println!("\n=== Testing PHP Namespace Resolution ===");

    let behavior = PhpBehavior::new();
    let file_id = FileId::new(1).unwrap();
    let mut ctx = behavior.create_resolution_context(file_id);

    // PHP namespace simulation
    // namespace App\Controllers;
    // use App\Models\User;
    // class UserController { }

    println!("Setting up PHP namespaces...");

    // Add namespace-level symbols
    ctx.add_symbol(
        "UserController".to_string(),
        SymbolId::new(1).unwrap(),
        ScopeLevel::Module,
    );
    ctx.add_symbol(
        "User".to_string(),
        SymbolId::new(2).unwrap(),
        ScopeLevel::Package,
    ); // imported

    // Global PHP functions
    ctx.add_symbol(
        "array_map".to_string(),
        SymbolId::new(100).unwrap(),
        ScopeLevel::Global,
    );

    // Test resolution
    assert_eq!(
        ctx.resolve("UserController"),
        Some(SymbolId::new(1).unwrap())
    );
    assert_eq!(ctx.resolve("User"), Some(SymbolId::new(2).unwrap()));
    assert_eq!(ctx.resolve("array_map"), Some(SymbolId::new(100).unwrap()));

    println!("✓ PHP namespace resolution works");

    // Test PHP-specific behaviors
    assert_eq!(behavior.module_separator(), "\\");
    assert!(behavior.supports_traits()); // PHP has traits
    assert!(!behavior.supports_inherent_methods()); // PHP doesn't have inherent methods
    println!("✓ PHP-specific behaviors correct");
}

/// Integration test that simulates real usage patterns
#[test]
fn test_cross_language_behavior_consistency() {
    println!("\n=== Testing Cross-Language Behavior Consistency ===");

    let typescript = TypeScriptBehavior::new();
    let python = PythonBehavior::new();
    let rust = RustBehavior::new();
    let php = PhpBehavior::new();

    // Test module path formatting
    println!("Module path formatting:");
    println!(
        "  TypeScript: {}",
        typescript.format_module_path("app/components", "Button")
    );
    println!(
        "  Python: {}",
        python.format_module_path("app.components", "Button")
    );
    println!(
        "  Rust: {}",
        rust.format_module_path("crate::components", "Button")
    );
    println!(
        "  PHP: {}",
        php.format_module_path("App\\Components", "Button")
    );

    // Test method call formatting
    println!("\nMethod call formatting:");
    println!(
        "  TypeScript: {}",
        typescript.format_method_call("obj", "method")
    );
    println!("  Python: {}", python.format_method_call("obj", "method"));
    println!("  Rust: {}", rust.format_method_call("obj", "method"));
    println!("  PHP: {}", php.format_method_call("obj", "method"));

    // Test inheritance relation naming
    println!("\nInheritance relation names:");
    println!("  TypeScript: {}", typescript.inheritance_relation_name());
    println!("  Python: {}", python.inheritance_relation_name());
    println!("  Rust: {}", rust.inheritance_relation_name());
    println!("  PHP: {}", php.inheritance_relation_name());

    println!("\n✓ All languages provide consistent behavior interfaces");
}

/// Verify that the new methods are actually callable
#[test]
fn test_new_behavior_methods_exist() {
    println!("\n=== Verifying New Behavior Methods ===");

    let behaviors: Vec<(&str, Box<dyn LanguageBehavior>)> = vec![
        ("TypeScript", Box::new(TypeScriptBehavior::new())),
        ("Python", Box::new(PythonBehavior::new())),
        ("Rust", Box::new(RustBehavior::new())),
        ("PHP", Box::new(PhpBehavior::new())),
    ];

    for (lang, behavior) in behaviors {
        println!("Testing {lang} behavior methods...");

        // Test resolution context creation
        let file_id = FileId::new(1).unwrap();
        let ctx = behavior.create_resolution_context(file_id);
        let _ = ctx.symbols_in_scope(); // Just verify it works without panic

        // Test inheritance resolver creation
        let resolver = behavior.create_inheritance_resolver();
        let chain = resolver.get_inheritance_chain("Test");
        assert!(chain.contains(&"Test".to_string()));

        // Test new methods with default implementations
        behavior.add_import(codanna::indexing::Import {
            path: "test/path".into(),
            alias: Some("test".into()),
            file_id,
            is_glob: false,
            is_type_only: false,
        });

        behavior.register_file(
            std::path::PathBuf::from("test.rs"),
            file_id,
            "test::module".to_string(),
        );

        behavior.add_trait_impl("MyType".to_string(), "MyTrait".to_string(), file_id);
        behavior.add_inherent_methods("MyType".to_string(), vec!["method".to_string()]);
        behavior.add_trait_methods("MyTrait".to_string(), vec!["trait_method".to_string()]);

        let trait_name = behavior.resolve_method_trait("MyType", "method");
        assert_eq!(trait_name, None); // Default implementation returns None

        let formatted = behavior.format_method_call("receiver", "method");
        assert!(!formatted.is_empty());

        let relation = behavior.inheritance_relation_name();
        assert!(!relation.is_empty());

        let kind = behavior.map_relationship("extends");
        // Just verify it returns something valid - actual mapping is language-specific
        assert!(matches!(
            kind,
            codanna::relationship::RelationKind::Extends
                | codanna::relationship::RelationKind::References
                | codanna::relationship::RelationKind::Implements
        ));

        println!("  ✓ All methods callable for {lang}");
    }

    println!("\n✓ All new behavior methods are properly implemented");
}
