//! Functional tests that showcase every error type with its helpful message
//!
//! These tests are designed to:
//! 1. Ensure every error type can be triggered
//! 2. Document what causes each error
//! 3. Verify that error messages are helpful and clear

use std::collections::HashSet;

use crate::{
    resolve::{
        convert::convert_file,
        names::NameTable,
        validate::{
            validate_behavior_tree_actions,
            validate_life_arc_transitions,
            validate_relationship_bonds,
            validate_schedule_overlaps,
            validate_trait_ranges,
        },
        ErrorCollector,
        ResolveError,
    },
    syntax::{
        ast::*,
        lexer::Lexer,
        FileParser,
    },
    Project,
};

// ===== Parse Errors =====

#[test]
fn test_unexpected_token_error() {
    let source = r#"
        character Martha {
            age 34
        }
    "#;
    // Missing colon after 'age' - should trigger UnexpectedToken

    let lexer = Lexer::new(source);
    let result = FileParser::new().parse(lexer);

    assert!(result.is_err(), "Should fail with unexpected token");
    println!("\n=== UnexpectedToken Error ===");
    if let Err(e) = result {
        println!("{:?}", e);
    }
}

#[test]
fn test_unexpected_eof_error() {
    let source = r#"
        character Martha {
            age: 34
    "#;
    // Missing closing brace - should trigger UnexpectedEof

    let lexer = Lexer::new(source);
    let result = FileParser::new().parse(lexer);

    assert!(result.is_err(), "Should fail with unexpected EOF");
    println!("\n=== UnexpectedEof Error ===");
    if let Err(e) = result {
        println!("{:?}", e);
    }
}

#[test]
fn test_invalid_token_error() {
    let source = "character Martha { age: @#$ }";
    // Invalid character sequence - should trigger InvalidToken

    let lexer = Lexer::new(source);
    let result = FileParser::new().parse(lexer);

    assert!(result.is_err(), "Should fail with invalid token");
    println!("\n=== InvalidToken Error ===");
    if let Err(e) = result {
        println!("{:?}", e);
    }
}

#[test]
fn test_unclosed_prose_block_error() {
    let source = r#"
        character Martha {
            backstory: ---backstory
This is Martha's backstory.
It goes on and on...
But it never closes!
        }
    "#;
    // Prose block never closed - should trigger UnclosedProseBlock

    let lexer = Lexer::new(source);
    let result = FileParser::new().parse(lexer);

    assert!(result.is_err(), "Should fail with unclosed prose block");
    println!("\n=== UnclosedProseBlock Error ===");
    if let Err(e) = result {
        println!("{:?}", e);
    }
}

// ===== Resolution Errors =====

#[test]
fn test_name_not_found_error() {
    let file = File {
        declarations: vec![],
    };

    let table = NameTable::from_file(&file).unwrap();
    let result = table.lookup(&["NonExistent".to_string()]);

    assert!(result.is_none(), "Should not find non-existent name");

    // Create the actual error
    let error = ResolveError::NameNotFound {
        name: "NonExistent".to_string(),
        suggestion: table.find_suggestion("NonExistent"),
    };

    println!("\n=== NameNotFound Error ===");
    println!("{:?}", error);
}

#[test]
fn test_duplicate_definition_error() {
    let file = File {
        declarations: vec![
            Declaration::Character(Character {
                name: "Martha".to_string(),
                species: None,
                fields: vec![],
                template: None,
                uses_behaviors: None,
                uses_schedule: None,
                span: Span::new(0, 10),
            }),
            Declaration::Character(Character {
                name: "Martha".to_string(),
                species: None,
                fields: vec![],
                template: None,
                uses_behaviors: None,
                uses_schedule: None,
                span: Span::new(20, 30),
            }),
        ],
    };

    let result = NameTable::from_file(&file);

    assert!(result.is_err(), "Should fail with duplicate definition");
    println!("\n=== DuplicateDefinition Error ===");
    if let Err(e) = result {
        println!("{:?}", e);
    }
}

#[test]
fn test_circular_dependency_error() {
    // Manually create a circular dependency error for demonstration
    let error = ResolveError::CircularDependency {
        cycle: "Template A -> Template B -> Template A".to_string(),
    };

    println!("\n=== CircularDependency Error ===");
    println!("{:?}", error);
}

#[test]
fn test_invalid_field_access_error() {
    let error = ResolveError::InvalidFieldAccess {
        message: "Field 'nonexistent' does not exist on character 'Martha'".to_string(),
    };

    println!("\n=== InvalidFieldAccess Error ===");
    println!("{:?}", error);
}

#[test]
fn test_type_mismatch_error() {
    let error = ResolveError::TypeMismatch {
        message: "Expected number for field 'age', but got string \"thirty\"".to_string(),
    };

    println!("\n=== TypeMismatch Error ===");
    println!("{:?}", error);
}

#[test]
fn test_validation_error_generic() {
    let error = ResolveError::ValidationError {
        message: "Cannot append field 'age': field already exists".to_string(),
        help: Some("The 'append' operation is used to add new fields. Use 'set' to update existing fields.".to_string()),
    };

    println!("\n=== ValidationError Error ===");
    println!("{:?}", error);
}

// ===== Validation Errors =====

#[test]
fn test_unknown_life_arc_state_error() {
    let life_arc = LifeArc {
        name: "Growth".to_string(),
        required_fields: vec![],
        states: vec![
            ArcState {
                name: "child".to_string(),
                on_enter: None,
                transitions: vec![Transition {
                    to: "adult".to_string(), // 'adult' exists
                    condition: Expr::BooleanLit(true),
                    span: Span::new(0, 10),
                }],
                span: Span::new(0, 50),
            },
            ArcState {
                name: "adult".to_string(),
                on_enter: None,
                transitions: vec![Transition {
                    to: "senior".to_string(), // 'senior' doesn't exist!
                    condition: Expr::BooleanLit(true),
                    span: Span::new(50, 60),
                }],
                span: Span::new(50, 100),
            },
        ],
        span: Span::new(0, 100),
    };

    let mut collector = ErrorCollector::new();
    validate_life_arc_transitions(&life_arc, &mut collector);

    assert!(collector.has_errors(), "Should fail with unknown state");
    println!("\n=== UnknownLifeArcState Error ===");
    if collector.has_errors() {
        let result = collector.into_result(());
        if let Err(e) = result {
            println!("{:?}", e);
        }
    }
}

#[test]
fn test_trait_out_of_range_error_bond() {
    let fields = vec![Field {
        name: "bond".to_string(),
        value: Value::Decimal(1.5), // Out of range!
        span: Span::new(0, 10),
    }];

    let mut collector = ErrorCollector::new();
    validate_trait_ranges(&fields, &mut collector);

    assert!(
        collector.has_errors(),
        "Should fail with out of range trait"
    );
    println!("\n=== TraitOutOfRange Error (bond too high) ===");
    if collector.has_errors() {
        let result = collector.into_result(());
        if let Err(e) = result {
            println!("{:?}", e);
        }
    }
}

#[test]
fn test_trait_out_of_range_error_age() {
    let fields = vec![Field {
        name: "age".to_string(),
        value: Value::Number(200), // Out of range!
        span: Span::new(0, 10),
    }];

    let mut collector = ErrorCollector::new();
    validate_trait_ranges(&fields, &mut collector);

    assert!(collector.has_errors(), "Should fail with out of range age");
    println!("\n=== TraitOutOfRange Error (age too high) ===");
    if collector.has_errors() {
        let result = collector.into_result(());
        if let Err(e) = result {
            println!("{:?}", e);
        }
    }
}

#[test]
fn test_trait_out_of_range_negative() {
    let fields = vec![Field {
        name: "trust".to_string(),
        value: Value::Decimal(-0.2), // Negative!
        span: Span::new(0, 10),
    }];

    let mut collector = ErrorCollector::new();
    validate_trait_ranges(&fields, &mut collector);

    assert!(collector.has_errors(), "Should fail with negative trait");
    println!("\n=== TraitOutOfRange Error (negative value) ===");
    if collector.has_errors() {
        let result = collector.into_result(());
        if let Err(e) = result {
            println!("{:?}", e);
        }
    }
}

#[test]
fn test_schedule_overlap_error() {
    let schedule = Schedule {
        name: "DailyRoutine".to_string(),
        modifies: None,
        fields: Vec::new(),
        recurrences: Vec::new(),
        blocks: vec![
            ScheduleBlock {
                name: None,
                is_override: false,
                activity: "work".to_string(),
                action: None,
                temporal_constraint: None,
                start: Time {
                    hour: 8,
                    minute: 0,
                    second: 0,
                },
                end: Time {
                    hour: 12,
                    minute: 30,
                    second: 0,
                },
                fields: vec![],
                span: Span::new(0, 50),
            },
            ScheduleBlock {
                name: None,
                is_override: false,
                activity: "lunch".to_string(),
                action: None,
                temporal_constraint: None,
                start: Time {
                    hour: 12,
                    minute: 0, // Overlaps with work!
                    second: 0,
                },
                end: Time {
                    hour: 13,
                    minute: 0,
                    second: 0,
                },
                fields: vec![],
                span: Span::new(50, 100),
            },
        ],
        span: Span::new(0, 100),
    };

    let mut collector = ErrorCollector::new();
    validate_schedule_overlaps(&schedule, &mut collector);

    assert!(collector.has_errors(), "Should fail with schedule overlap");
    println!("\n=== ScheduleOverlap Error ===");
    if collector.has_errors() {
        let result = collector.into_result(());
        if let Err(e) = result {
            println!("{:?}", e);
        }
    }
}

#[test]
fn test_unknown_behavior_action_error() {
    let tree = Behavior {
        name: "WorkDay".to_string(),
        root: BehaviorNode::Action("unknown_action".to_string(), vec![]),
        span: Span::new(0, 50),
    };

    // Create a registry with some known actions (but not "unknown_action")
    let mut action_registry = HashSet::new();
    action_registry.insert("walk".to_string());
    action_registry.insert("work".to_string());
    action_registry.insert("eat".to_string());

    let mut collector = ErrorCollector::new();
    validate_behavior_tree_actions(&tree, &action_registry, &mut collector);

    assert!(collector.has_errors(), "Should fail with unknown action");
    println!("\n=== UnknownBehaviorAction Error ===");
    if collector.has_errors() {
        let result = collector.into_result(());
        if let Err(e) = result {
            println!("{:?}", e);
        }
    }
}

#[test]
fn test_relationship_bond_out_of_range() {
    let relationship = Relationship {
        name: "Test".to_string(),
        participants: vec![],
        fields: vec![Field {
            name: "bond".to_string(),
            value: Value::Decimal(2.5), // Way out of range!
            span: Span::new(0, 10),
        }],
        span: Span::new(0, 50),
    };

    let mut collector = ErrorCollector::new();
    validate_relationship_bonds(&[relationship], &mut collector);

    assert!(collector.has_errors(), "Should fail with bond out of range");
    println!("\n=== Relationship Bond Out of Range ===");
    if collector.has_errors() {
        let result = collector.into_result(());
        if let Err(e) = result {
            println!("{:?}", e);
        }
    }
}

#[test]
fn test_duplicate_field_in_convert() {
    let character = Character {
        name: "Martha".to_string(),
        species: None,
        fields: vec![
            Field {
                name: "age".to_string(),
                value: Value::Number(34),
                span: Span::new(0, 10),
            },
            Field {
                name: "age".to_string(), // Duplicate!
                value: Value::Number(35),
                span: Span::new(10, 20),
            },
        ],
        template: None,
        uses_behaviors: None,
        uses_schedule: None,
        span: Span::new(0, 50),
    };

    let file = File {
        declarations: vec![Declaration::Character(character)],
    };

    let result = convert_file(&file);

    assert!(result.is_err(), "Should fail with duplicate field");
    println!("\n=== Duplicate Field Error (in conversion) ===");
    if let Err(e) = result {
        println!("{:?}", e);
    }
}

// ===== Project Errors =====

#[test]
fn test_invalid_project_structure_no_directory() {
    let result = Project::load("/nonexistent/path/to/project");

    assert!(result.is_err(), "Should fail with invalid structure");
    println!("\n=== InvalidStructure Error (directory doesn't exist) ===");
    if let Err(e) = result {
        println!("{:?}", e);
    }
}

#[test]
fn test_invalid_project_structure_not_directory() {
    // Try to load a file as if it were a directory
    let result = Project::load("Cargo.toml");

    assert!(result.is_err(), "Should fail - file not directory");
    println!("\n=== InvalidStructure Error (not a directory) ===");
    if let Err(e) = result {
        println!("{:?}", e);
    }
}

// ===== Showcase All Errors =====

#[test]
#[ignore] // Run with: cargo test error_showcase -- --ignored --nocapture
fn error_showcase_all() {
    println!("\n\n");
    println!("╔════════════════════════════════════════════════════════════════╗");
    println!("║         STORYBOOK ERROR MESSAGES SHOWCASE                      ║");
    println!("║    Every error type with helpful hints for users               ║");
    println!("╚════════════════════════════════════════════════════════════════╝");

    test_unexpected_token_error();
    test_unexpected_eof_error();
    test_invalid_token_error();
    test_unclosed_prose_block_error();
    test_name_not_found_error();
    test_duplicate_definition_error();
    test_circular_dependency_error();
    test_invalid_field_access_error();
    test_type_mismatch_error();
    test_validation_error_generic();
    test_unknown_life_arc_state_error();
    test_trait_out_of_range_error_bond();
    test_trait_out_of_range_error_age();
    test_trait_out_of_range_negative();
    test_schedule_overlap_error();
    test_unknown_behavior_action_error();
    test_relationship_bond_out_of_range();
    test_duplicate_field_in_convert();
    test_invalid_project_structure_no_directory();
    test_invalid_project_structure_not_directory();

    println!("\n\n");
    println!("╔════════════════════════════════════════════════════════════════╗");
    println!("║                    SHOWCASE COMPLETE                           ║");
    println!("╚════════════════════════════════════════════════════════════════╝");
}
