//! Example Validation Tests
//!
//! These tests validate that example storybooks load correctly and demonstrate
//! documented features. Unlike unit/integration tests that validate specific
//! functionality, these tests ensure that:
//! 1. Examples parse and resolve successfully
//! 2. Examples demonstrate features as documented
//! 3. Examples serve as reliable educational resources
//!
//! Cross-references:
//! - examples/baker-family/README.md
//! - examples/alice-in-wonderland/README.md
//! - DEVELOPER-GUIDE.md

use std::path::PathBuf;

use storybook::Project;

/// Helper to load an example project from the examples/ directory
fn load_example(name: &str) -> Project {
    let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    path.push("examples");
    path.push(name);

    assert!(path.exists(), "Example '{}' not found at {:?}", name, path);

    Project::load(&path).unwrap_or_else(|_| panic!("Failed to load example '{}'", name))
}

// ============================================================================
// Baker Family Example Tests
// ============================================================================

#[test]
fn test_baker_family_loads_successfully() {
    let project = load_example("baker-family");

    // Should have exactly 3 characters
    let char_count = project.characters().count();
    assert_eq!(char_count, 3, "Baker family should have 3 characters");

    // Verify all characters exist
    assert!(
        project.find_character("Martha").is_some(),
        "Martha should exist"
    );
    assert!(
        project.find_character("Jane").is_some(),
        "Jane should exist"
    );
    assert!(
        project.find_character("Emma").is_some(),
        "Emma should exist"
    );
}

#[test]
fn test_baker_family_martha_has_all_template_fields() {
    let project = load_example("baker-family");
    let martha = project
        .find_character("Martha")
        .expect("Martha should exist");

    // Fields from Person template (base)
    assert!(
        martha.fields.contains_key("age"),
        "Should have age from Person"
    );
    assert!(
        martha.fields.contains_key("energy"),
        "Should have energy from Person"
    );
    assert!(
        martha.fields.contains_key("mood"),
        "Should have mood from Person"
    );

    // Fields from Worker template (extends Person)
    assert!(
        martha.fields.contains_key("occupation"),
        "Should have occupation from Worker"
    );
    assert!(
        martha.fields.contains_key("work_ethic"),
        "Should have work_ethic from Worker"
    );

    // Fields from Baker template (extends Worker)
    assert!(
        martha.fields.contains_key("specialty"),
        "Should have specialty from Baker"
    );
    assert!(
        martha.fields.contains_key("baking_skill"),
        "Should have baking_skill from Baker"
    );
    assert!(
        martha.fields.contains_key("customer_relations"),
        "Should have customer_relations from Baker"
    );
}

#[test]
fn test_baker_family_field_values() {
    let project = load_example("baker-family");
    let martha = project.find_character("Martha").unwrap();

    // Martha sets age: 34 (overriding Person template range 0..100)
    if let Some(storybook::syntax::ast::Value::Number(age)) = martha.fields.get("age") {
        assert_eq!(*age, 34, "Martha's age should be 34");
    } else {
        panic!("age should be a Number");
    }

    // Martha sets specialty: "sourdough" (overriding Baker template default
    // "bread")
    if let Some(storybook::syntax::ast::Value::Text(specialty)) = martha.fields.get("specialty") {
        assert_eq!(
            specialty, "sourdough",
            "Martha's specialty should be sourdough"
        );
    } else {
        panic!("specialty should be a String");
    }
}

#[test]
fn test_baker_family_has_expected_behaviors() {
    let project = load_example("baker-family");

    let behaviors: Vec<_> = project.behaviors().collect();
    let behavior_names: Vec<_> = behaviors.iter().map(|b| b.name.as_str()).collect();

    // Core behaviors for the family
    let expected = vec![
        "BakingWork",
        "PrepKitchen",
        "BasicNeeds",
        "SocialInteraction",
        "BakingSkills",
        "CustomerService",
        "PlayBehavior",
        "LearnBehavior",
    ];

    for name in expected {
        assert!(
            behavior_names.contains(&name),
            "Should have behavior '{}', got: {:?}",
            name,
            behavior_names
        );
    }
}

#[test]
fn test_baker_family_has_expected_schedules() {
    let project = load_example("baker-family");

    let schedules: Vec<_> = project.schedules().collect();
    assert!(!schedules.is_empty(), "Should have schedules");

    let schedule_names: Vec<_> = schedules.iter().map(|s| s.name.as_str()).collect();

    // Should have work schedules
    assert!(
        schedule_names
            .iter()
            .any(|&n| n.contains("Schedule") || n.contains("Week")),
        "Should have schedule definitions, got: {:?}",
        schedule_names
    );
}

#[test]
fn test_baker_family_multi_file_structure() {
    // Baker family is organized across multiple files:
    // - schema/templates.sb
    // - behaviors/baker_behaviors.sb
    // - schedules/work_schedules.sb
    // - characters/*.sb

    let project = load_example("baker-family");

    // If all declaration types are present, multi-file loading works
    assert!(project.characters().count() > 0, "Should have characters");
    assert!(project.behaviors().count() > 0, "Should have behaviors");
    assert!(project.schedules().count() > 0, "Should have schedules");
}

// ============================================================================
// Alice in Wonderland Example Tests
// ============================================================================

#[test]
fn test_alice_wonderland_loads_successfully() {
    let project = load_example("alice-in-wonderland");

    // Should have multiple whimsical characters
    let char_count = project.characters().count();
    assert!(
        char_count >= 5,
        "Wonderland should have at least 5 characters, got {}",
        char_count
    );

    // Verify key characters
    assert!(
        project.find_character("Alice").is_some(),
        "Alice should exist"
    );
    assert!(
        project.find_character("WhiteRabbit").is_some(),
        "WhiteRabbit should exist"
    );
    assert!(
        project.find_character("CheshireCat").is_some(),
        "CheshireCat should exist"
    );
}

#[test]
fn test_alice_wonderland_alice_details() {
    let project = load_example("alice-in-wonderland");
    let alice = project.find_character("Alice").expect("Alice should exist");

    // Alice should have age field
    assert!(alice.fields.contains_key("age"), "Alice should have age");

    // Alice should have personality/trait fields
    assert!(
        !alice.fields.is_empty(),
        "Alice should have character fields"
    );
}

#[test]
fn test_alice_wonderland_has_behaviors() {
    let project = load_example("alice-in-wonderland");

    let behavior_count = project.behaviors().count();
    assert!(behavior_count > 0, "Wonderland should have behaviors");
}

#[test]
fn test_alice_wonderland_has_schedules() {
    let project = load_example("alice-in-wonderland");

    let schedule_count = project.schedules().count();
    assert!(schedule_count > 0, "Wonderland should have schedules");
}

#[test]
fn test_alice_wonderland_multi_file_organization() {
    // Alice in Wonderland is organized into:
    // - schema/ (templates, enums)
    // - world/characters/ (character definitions)
    // - world/behaviors/ (behavior trees)
    // - world/schedules/ (schedules)

    let project = load_example("alice-in-wonderland");

    // Verify all major declaration types loaded
    assert!(
        project.characters().count() >= 5,
        "Should have multiple characters"
    );
    assert!(project.behaviors().count() > 0, "Should have behaviors");
    assert!(project.schedules().count() > 0, "Should have schedules");
}

#[test]
fn test_alice_wonderland_all_characters_have_names_and_fields() {
    let project = load_example("alice-in-wonderland");

    for character in project.characters() {
        assert!(!character.name.is_empty(), "Character should have a name");
        assert!(
            !character.fields.is_empty(),
            "Character {} should have fields",
            character.name
        );
    }
}

#[test]
fn test_alice_wonderland_schedules_have_valid_blocks() {
    let project = load_example("alice-in-wonderland");

    for schedule in project.schedules() {
        if schedule.blocks.is_empty() {
            continue; // Some schedules might be empty/templates
        }

        for block in &schedule.blocks {
            // Time ranges should be valid: start < end (or midnight wrap)
            let start_mins = block.start.hour as u32 * 60 + block.start.minute as u32;
            let end_mins = block.end.hour as u32 * 60 + block.end.minute as u32;

            assert!(
                start_mins < end_mins || end_mins == 0,
                "Block in {} should have valid time: {:02}:{:02}-{:02}:{:02}",
                schedule.name,
                block.start.hour,
                block.start.minute,
                block.end.hour,
                block.end.minute
            );
        }
    }
}

#[test]
fn test_alice_wonderland_behaviors_have_valid_structure() {
    let project = load_example("alice-in-wonderland");

    for behavior in project.behaviors() {
        assert!(!behavior.name.is_empty(), "Behavior should have a name");
        // Just verify root node exists
        let _ = &behavior.root;
    }
}

// ============================================================================
// Cross-Example Consistency Tests
// ============================================================================

#[test]
fn test_all_examples_load_without_errors() {
    // Test that all examples in the examples/ directory load successfully
    let examples = vec!["baker-family", "alice-in-wonderland"];

    for name in examples {
        let result = std::panic::catch_unwind(|| {
            load_example(name);
        });

        assert!(
            result.is_ok(),
            "Example '{}' should load without panicking",
            name
        );
    }
}

#[test]
fn test_examples_have_consistent_structure() {
    // All examples should have characters and at least one other declaration type
    let examples = vec!["baker-family", "alice-in-wonderland"];

    for name in examples {
        let project = load_example(name);

        assert!(
            project.characters().count() > 0,
            "Example '{}' should have characters",
            name
        );

        let has_other_decls = project.behaviors().count() > 0 ||
            project.schedules().count() > 0 ||
            project.life_arcs().count() > 0;

        assert!(
            has_other_decls,
            "Example '{}' should have behaviors, schedules, or life arcs",
            name
        );
    }
}
