//! Semantic validation for Storybook entities
//!
//! This module validates semantic constraints that can't be checked during
//! parsing:
//! - Reserved keyword conflicts in field names
//! - Trait value ranges
//! - Schedule time overlaps
//! - Life arc transition validity
//! - Behavior tree action registry checks
//! - Relationship bond values (0.0 .. 1.0)

use std::collections::HashSet;

use crate::{
    resolve::{
        ErrorCollector,
        ResolveError,
        Result,
    },
    syntax::ast::*,
};

/// List of reserved keywords that cannot be used as field names
const RESERVED_KEYWORDS: &[&str] = &[
    // Top-level declaration keywords
    "character",
    "template",
    "life_arc",
    "schedule",
    "behavior",
    "institution",
    "relationship",
    "location",
    "species",
    "enum",
    // Statement keywords
    "use",
    "state",
    "on",
    "as",
    "remove",
    "append",
    "strict",
    "include",
    "from",
    // Expression keywords
    "self",
    "other",
    "forall",
    "exists",
    "in",
    "where",
    "and",
    "or",
    "not",
    "is",
    "true",
    "false",
];

/// Validate that field names don't conflict with reserved keywords
pub fn validate_no_reserved_keywords(fields: &[Field], collector: &mut ErrorCollector) {
    for field in fields {
        if RESERVED_KEYWORDS.contains(&field.name.as_str()) {
            collector.add(ResolveError::ValidationError {
                message: format!(
                    "Field name '{}' is a reserved keyword and cannot be used",
                    field.name
                ),
                help: Some(format!(
                    "The name '{}' is reserved by the Storybook language. Try using a different name like '{}Type', '{}Value', or 'my{}'.",
                    field.name,
                    field.name,
                    field.name,
                    field.name.chars().next().unwrap_or('x').to_uppercase().collect::<String>() + &field.name[1..]
                )),
            });
        }
    }
}

/// Validate trait values are within valid ranges
pub fn validate_trait_ranges(fields: &[Field], collector: &mut ErrorCollector) {
    for field in fields {
        match &field.value {
            | Value::Decimal(f) => {
                // Normalized trait values should be 0.0 .. 1.0
                if (field.name.ends_with("_normalized") ||
                    field.name == "bond" ||
                    field.name == "trust" ||
                    field.name == "love") &&
                    !(0.0..=1.0).contains(f)
                {
                    collector.add(ResolveError::TraitOutOfRange {
                        field: field.name.clone(),
                        value: f.to_string(),
                        min: 0.0,
                        max: 1.0,
                    });
                }
            },
            | Value::Number(i) => {
                // Age should be reasonable
                if field.name == "age" && (*i < 0 || *i > 150) {
                    collector.add(ResolveError::TraitOutOfRange {
                        field: "age".to_string(),
                        value: i.to_string(),
                        min: 0.0,
                        max: 150.0,
                    });
                }
            },
            | _ => {},
        }
    }
}

/// Validate relationship bond values are in [0.0, 1.0]
pub fn validate_relationship_bonds(relationships: &[Relationship], collector: &mut ErrorCollector) {
    for rel in relationships {
        for field in &rel.fields {
            if field.name == "bond" {
                if let Value::Decimal(f) = field.value {
                    if !(0.0..=1.0).contains(&f) {
                        collector.add(ResolveError::TraitOutOfRange {
                            field: "bond".to_string(),
                            value: f.to_string(),
                            min: 0.0,
                            max: 1.0,
                        });
                    }
                }
            }
        }

        // Validate participant fields
        for participant in &rel.participants {
            validate_trait_ranges(&participant.fields, collector);
        }
    }
}

/// Validate schedule blocks don't overlap in time
pub fn validate_schedule_overlaps(schedule: &Schedule, collector: &mut ErrorCollector) {
    // Sort blocks by start time
    let mut sorted_blocks: Vec<_> = schedule.blocks.iter().collect();
    sorted_blocks.sort_by_key(|b| (b.start.hour as u32) * 60 + (b.start.minute as u32));

    // Check for overlaps
    for i in 0..sorted_blocks.len() {
        for j in (i + 1)..sorted_blocks.len() {
            let block1 = sorted_blocks[i];
            let block2 = sorted_blocks[j];

            let end1 = (block1.end.hour as u32) * 60 + (block1.end.minute as u32);
            let start2 = (block2.start.hour as u32) * 60 + (block2.start.minute as u32);

            // Check if blocks overlap
            if end1 > start2 {
                collector.add(ResolveError::ScheduleOverlap {
                    block1: format!(
                        "{} ({}:{:02}-{}:{:02})",
                        block1.name.as_ref().unwrap_or(&block1.activity),
                        block1.start.hour,
                        block1.start.minute,
                        block1.end.hour,
                        block1.end.minute
                    ),
                    block2: format!(
                        "{} ({}:{:02}-{}:{:02})",
                        block2.name.as_ref().unwrap_or(&block2.activity),
                        block2.start.hour,
                        block2.start.minute,
                        block2.end.hour,
                        block2.end.minute
                    ),
                    end1: format!("{}:{:02}", block1.end.hour, block1.end.minute),
                    start2: format!("{}:{:02}", block2.start.hour, block2.start.minute),
                });
            }
        }
    }
}

/// Validate life arc state machine has valid transitions
pub fn validate_life_arc_transitions(life_arc: &LifeArc, collector: &mut ErrorCollector) {
    // Collect all state names
    let mut state_names = HashSet::new();
    for state in &life_arc.states {
        state_names.insert(state.name.clone());
    }

    // Validate all transitions point to valid states
    for state in &life_arc.states {
        for transition in &state.transitions {
            if !state_names.contains(&transition.to) {
                let available_states = state_names
                    .iter()
                    .map(|s| format!("'{}'", s))
                    .collect::<Vec<_>>()
                    .join(", ");

                collector.add(ResolveError::UnknownLifeArcState {
                    life_arc: life_arc.name.clone(),
                    state: state.name.clone(),
                    target: transition.to.clone(),
                    available_states,
                });
            }
        }
    }

    // Warn if states have no outgoing transitions (terminal states)
    // This is not an error, just informational
}

/// Validate behavior tree actions are known
///
/// If action_registry is empty, skips validation (no schema provided).
pub fn validate_behavior_tree_actions(
    tree: &Behavior,
    action_registry: &HashSet<String>,
    collector: &mut ErrorCollector,
) {
    // Skip validation if no action schema was provided
    if action_registry.is_empty() {
        return;
    }

    validate_tree_node_actions(&tree.root, action_registry, &tree.name, collector)
}

fn validate_tree_node_actions(
    node: &BehaviorNode,
    action_registry: &HashSet<String>,
    tree_name: &str,
    collector: &mut ErrorCollector,
) {
    match node {
        | BehaviorNode::Sequence { children, .. } | BehaviorNode::Selector { children, .. } => {
            for child in children {
                validate_tree_node_actions(child, action_registry, tree_name, collector);
            }
        },
        | BehaviorNode::Action(name, _params) => {
            if !action_registry.contains(name) {
                collector.add(ResolveError::UnknownBehaviorAction {
                    tree: tree_name.to_string(),
                    action: name.clone(),
                });
            }
        },
        | BehaviorNode::Condition(_) => {
            // Conditions are validated separately via expression validation
        },
        | BehaviorNode::Decorator { child, .. } => {
            validate_tree_node_actions(child, action_registry, tree_name, collector);
        },
        | BehaviorNode::SubTree(_path) => {
            // SubTree references validated separately
        },
    }
}

/// Validate character resource linking
///
/// Checks:
/// 1. No duplicate behavior tree references
/// 2. Priority values are valid (handled by type system)
pub fn validate_character_resource_links(character: &Character, collector: &mut ErrorCollector) {
    // Check for duplicate behavior tree references
    if let Some(ref behavior_links) = character.uses_behaviors {
        let mut seen_trees: HashSet<String> = HashSet::new();

        for link in behavior_links {
            let tree_name = link.tree.join("::");

            if seen_trees.contains(&tree_name) {
                collector.add(ResolveError::ValidationError {
                    message: format!(
                        "Character '{}' has duplicate behavior tree reference: {}",
                        character.name,
                        tree_name
                    ),
                    help: Some(format!(
                        "The behavior tree '{}' is referenced multiple times in the uses behaviors list. Each behavior tree should only be referenced once. If you want different conditions or priorities, combine them into a single entry.",
                        tree_name
                    )),
                });
            }

            seen_trees.insert(tree_name);
        }
    }

    // Check for duplicate schedule references
    if let Some(ref schedules) = character.uses_schedule {
        let mut seen_schedules: HashSet<String> = HashSet::new();

        for schedule in schedules {
            if seen_schedules.contains(schedule) {
                collector.add(ResolveError::ValidationError {
                    message: format!(
                        "Character '{}' has duplicate schedule reference: {}",
                        character.name,
                        schedule
                    ),
                    help: Some(format!(
                        "The schedule '{}' is referenced multiple times. Each schedule should only be referenced once.",
                        schedule
                    )),
                });
            }

            seen_schedules.insert(schedule.clone());
        }
    }
}

/// Validate institution resource linking
pub fn validate_institution_resource_links(
    institution: &Institution,
    collector: &mut ErrorCollector,
) {
    // Check for duplicate behavior tree references
    if let Some(ref behavior_links) = institution.uses_behaviors {
        let mut seen_trees: HashSet<String> = HashSet::new();

        for link in behavior_links {
            let tree_name = link.tree.join("::");

            if seen_trees.contains(&tree_name) {
                collector.add(ResolveError::ValidationError {
                    message: format!(
                        "Institution '{}' has duplicate behavior tree reference: {}",
                        institution.name,
                        tree_name
                    ),
                    help: Some(format!(
                        "The behavior tree '{}' is referenced multiple times. Each behavior tree should only be referenced once.",
                        tree_name
                    )),
                });
            }

            seen_trees.insert(tree_name);
        }
    }

    // Check for duplicate schedule references
    if let Some(ref schedules) = institution.uses_schedule {
        let mut seen_schedules: HashSet<String> = HashSet::new();

        for schedule in schedules {
            if seen_schedules.contains(schedule) {
                collector.add(ResolveError::ValidationError {
                    message: format!(
                        "Institution '{}' has duplicate schedule reference: {}",
                        institution.name, schedule
                    ),
                    help: Some(format!(
                        "The schedule '{}' is referenced multiple times.",
                        schedule
                    )),
                });
            }

            seen_schedules.insert(schedule.clone());
        }
    }
}

/// Validate schedule composition requirements
///
/// Checks:
/// 1. All blocks in modified schedules have names
/// 2. Override blocks reference valid block names (requires name resolution)
pub fn validate_schedule_composition(schedule: &Schedule, collector: &mut ErrorCollector) {
    // If schedule modifies another, all blocks must have names for override system
    if schedule.modifies.is_some() {
        for block in &schedule.blocks {
            if block.name.is_none() && !block.activity.is_empty() {
                collector.add(ResolveError::ValidationError {
                    message: format!(
                        "Schedule '{}' modifies another schedule but has unnamed blocks",
                        schedule.name
                    ),
                    help: Some(
                        "When a schedule modifies another, all blocks must have names to support the override system. Use 'block name { ... }' syntax instead of 'time -> time : activity { ... }'.".to_string()
                    ),
                });
            }
        }
    }

    // Validate that new-style blocks have action references
    for block in &schedule.blocks {
        if block.name.is_some() && block.action.is_none() && block.activity.is_empty() {
            collector.add(ResolveError::ValidationError {
                message: format!(
                    "Schedule '{}' block '{}' missing action reference",
                    schedule.name,
                    block.name.as_ref().unwrap()
                ),
                help: Some(
                    "Named blocks should specify a behavior using 'action: BehaviorName'. Example: 'block work { 9:00 -> 17:00, action: WorkBehavior }'".to_string()
                ),
            });
        }
    }
}

/// Validate that all sub_concepts reference existing parent concepts
pub fn validate_sub_concept_parents(file: &File, collector: &mut ErrorCollector) {
    // Collect all concept names
    let concept_names: HashSet<String> = file
        .declarations
        .iter()
        .filter_map(|decl| {
            if let Declaration::Concept(c) = decl {
                Some(c.name.clone())
            } else {
                None
            }
        })
        .collect();

    // Check each sub_concept references an existing parent
    for decl in &file.declarations {
        if let Declaration::SubConcept(sc) = decl {
            if !concept_names.contains(&sc.parent_concept) {
                collector.add(ResolveError::ValidationError {
                    message: format!(
                        "Parent concept '{}' not found for sub_concept '{}.{}'",
                        sc.parent_concept, sc.parent_concept, sc.name
                    ),
                    help: Some(format!(
                        "Add 'concept {}' before defining sub_concepts for it.",
                        sc.parent_concept
                    )),
                });
            }
        }
    }
}

/// Get the type name of a Value for error messages
fn value_type_name(value: &Value) -> &'static str {
    match value {
        | Value::Number(_) => "Number",
        | Value::Decimal(_) => "Decimal",
        | Value::Text(_) => "Text",
        | Value::Boolean(_) => "Boolean",
        | Value::Range(_, _) => "Range",
        | Value::Time(_) => "Time",
        | Value::Duration(_) => "Duration",
        | Value::Identifier(_) => "Identifier",
        | Value::List(_) => "List",
        | Value::Object(_) => "Object",
        | Value::ProseBlock(_) => "ProseBlock",
        | Value::Override(_) => "Override",
        | Value::Any => "Any",
    }
}

/// Check if two values have compatible types
fn values_type_compatible(a: &Value, b: &Value) -> bool {
    matches!(
        (a, b),
        (Value::Number(_), Value::Number(_)) |
            (Value::Decimal(_), Value::Decimal(_)) |
            (Value::Text(_), Value::Text(_)) |
            (Value::Boolean(_), Value::Boolean(_)) |
            (Value::Time(_), Value::Time(_)) |
            (Value::Duration(_), Value::Duration(_)) |
            (Value::Identifier(_), Value::Identifier(_)) |
            (Value::List(_), Value::List(_)) |
            (Value::Object(_), Value::Object(_)) |
            (Value::ProseBlock(_), Value::ProseBlock(_)) |
            (Value::Range(_, _), Value::Range(_, _)) |
            (Value::Range(_, _), Value::Number(_)) |
            (Value::Number(_), Value::Range(_, _)) |
            (Value::Range(_, _), Value::Decimal(_)) |
            (Value::Decimal(_), Value::Range(_, _)) |
            (Value::Any, _) |
            (_, Value::Any)
    )
}

/// Validate type invariance in species inheritance
///
/// When a template extends a species, any shared field names must have
/// compatible types. For example, if species Human has `age: 0` (Number),
/// then template Person: Human cannot have `age: "old"` (Text).
pub fn validate_species_type_invariance(file: &File, collector: &mut ErrorCollector) {
    for decl in &file.declarations {
        if let Declaration::Template(template) = decl {
            if let Some(species_name) = &template.species_base {
                // Find the species declaration
                let species = file.declarations.iter().find_map(|d| {
                    if let Declaration::Species(s) = d {
                        if s.name == *species_name {
                            return Some(s);
                        }
                    }
                    None
                });

                if let Some(species) = species {
                    // Check each template field against species fields
                    for template_field in &template.fields {
                        if let Some(species_field) = species
                            .fields
                            .iter()
                            .find(|f| f.name == template_field.name)
                        {
                            if !values_type_compatible(&species_field.value, &template_field.value)
                            {
                                collector.add(ResolveError::ValidationError {
                                    message: format!(
                                        "Field '{}' has type {} in species {} but {} in template {}",
                                        template_field.name,
                                        value_type_name(&species_field.value),
                                        species_name,
                                        value_type_name(&template_field.value),
                                        template.name,
                                    ),
                                    help: Some(format!(
                                        "Template '{}' inherits from species '{}'. Field '{}' must keep the same type ({}) as defined in the species.",
                                        template.name,
                                        species_name,
                                        template_field.name,
                                        value_type_name(&species_field.value),
                                    )),
                                });
                            }
                        }
                    }
                }
            }
        }
    }
}

/// Validate definition patterns against the concept registry
///
/// Checks:
/// 1. The definition name references an existing concept
/// 2. All variant names in patterns exist in the concept's enum sub-concepts
/// 3. Patterns are exhaustive (all variants are covered)
pub fn validate_definition_patterns(
    file: &File,
    registry: &crate::resolve::types::ConceptRegistry,
    collector: &mut ErrorCollector,
) {
    for decl in &file.declarations {
        if let Declaration::Definition(comp) = decl {
            // Check that the concept exists
            let concept = match registry.lookup_concept(&comp.name) {
                | Some(c) => c,
                | None => {
                    collector.add(ResolveError::ValidationError {
                        message: format!("Concept '{}' not found for definition", comp.name),
                        help: Some(format!(
                            "Add 'concept {}' before defining a definition for it.",
                            comp.name
                        )),
                    });
                    continue;
                },
            };

            // Collect all enum variants from all enum sub-concepts
            let mut all_variants: HashSet<String> = HashSet::new();
            for sc_info in concept.sub_concepts.values() {
                if let SubConceptKind::Enum { variants } = &sc_info.kind {
                    for variant in variants {
                        all_variants.insert(variant.clone());
                    }
                }
            }

            // Check each variant pattern references valid variants
            let mut covered_variants: HashSet<String> = HashSet::new();
            for pattern in &comp.variants {
                // Check that the variant name is a known variant
                if !all_variants.contains(&pattern.name) {
                    collector.add(ResolveError::ValidationError {
                        message: format!(
                            "Variant '{}' not found in concept '{}'",
                            pattern.name, comp.name
                        ),
                        help: Some(format!(
                            "Available variants for '{}': {}",
                            comp.name,
                            if all_variants.is_empty() {
                                "(none - add sub_concept with enum variants)".to_string()
                            } else {
                                let mut sorted: Vec<_> = all_variants.iter().collect();
                                sorted.sort();
                                sorted
                                    .iter()
                                    .map(|s| s.as_str())
                                    .collect::<Vec<_>>()
                                    .join(", ")
                            }
                        )),
                    });
                } else {
                    covered_variants.insert(pattern.name.clone());
                }

                // Check field conditions reference valid fields in the Is values
                for condition in &pattern.conditions {
                    if let Condition::Is(values) = &condition.condition {
                        for value in values {
                            // Check that the value references a valid variant
                            if !all_variants.contains(value) {
                                collector.add(ResolveError::ValidationError {
                                    message: format!(
                                        "Value '{}' in condition for variant '{}' not found in concept '{}'",
                                        value, pattern.name, comp.name
                                    ),
                                    help: Some(format!(
                                        "Available variants: {}",
                                        {
                                            let mut sorted: Vec<_> = all_variants.iter().collect();
                                            sorted.sort();
                                            sorted.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(", ")
                                        }
                                    )),
                                });
                            }
                        }
                    }
                }
            }

            // Check exhaustiveness - all variants should be covered
            let uncovered: Vec<_> = all_variants.difference(&covered_variants).collect();
            if !uncovered.is_empty() {
                let mut sorted: Vec<_> = uncovered.into_iter().collect();
                sorted.sort();
                collector.add(ResolveError::ValidationError {
                    message: format!(
                        "definition '{}' is not exhaustive: missing variants {}",
                        comp.name,
                        sorted
                            .iter()
                            .map(|s| format!("'{}'", s))
                            .collect::<Vec<_>>()
                            .join(", ")
                    ),
                    help: Some(format!(
                        "Add patterns for all variants of concept '{}'. Missing: {}",
                        comp.name,
                        sorted
                            .iter()
                            .map(|s| s.as_str())
                            .collect::<Vec<_>>()
                            .join(", ")
                    )),
                });
            }
        }
    }
}

/// Validate an entire file
///
/// Collects all validation errors and returns them together instead of failing
/// fast.
pub fn validate_file(file: &File, action_registry: &HashSet<String>) -> Result<()> {
    let mut collector = ErrorCollector::new();

    // Type system validation
    validate_sub_concept_parents(file, &mut collector);
    validate_species_type_invariance(file, &mut collector);
    let concept_registry = crate::resolve::types::ConceptRegistry::from_file(file);
    validate_definition_patterns(file, &concept_registry, &mut collector);

    for decl in &file.declarations {
        match decl {
            | Declaration::Character(c) => {
                validate_trait_ranges(&c.fields, &mut collector);
                validate_character_resource_links(c, &mut collector);
            },
            | Declaration::Institution(i) => {
                validate_institution_resource_links(i, &mut collector);
            },
            | Declaration::Relationship(r) => {
                validate_relationship_bonds(std::slice::from_ref(r), &mut collector);
            },
            | Declaration::Schedule(s) => {
                validate_schedule_overlaps(s, &mut collector);
                validate_schedule_composition(s, &mut collector);
            },
            | Declaration::LifeArc(la) => {
                validate_life_arc_transitions(la, &mut collector);
            },
            | Declaration::Behavior(bt) => {
                validate_behavior_tree_actions(bt, action_registry, &mut collector);
            },
            | _ => {
                // Other declarations don't need validation yet
            },
        }
    }

    collector.into_result(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_valid_trait_ranges() {
        let fields = vec![
            Field {
                name: "bond".to_string(),
                value: Value::Decimal(0.8),
                span: Span::new(0, 10),
            },
            Field {
                name: "age".to_string(),
                value: Value::Number(30),
                span: Span::new(0, 10),
            },
        ];

        let mut collector = ErrorCollector::new();
        validate_trait_ranges(&fields, &mut collector);
        assert!(!collector.has_errors());
    }

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

        let mut collector = ErrorCollector::new();
        validate_trait_ranges(&fields, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_invalid_bond_value_negative() {
        let fields = vec![Field {
            name: "bond".to_string(),
            value: Value::Decimal(-0.1),
            span: Span::new(0, 10),
        }];

        let mut collector = ErrorCollector::new();
        validate_trait_ranges(&fields, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_invalid_age_negative() {
        let fields = vec![Field {
            name: "age".to_string(),
            value: Value::Number(-5),
            span: Span::new(0, 10),
        }];

        let mut collector = ErrorCollector::new();
        validate_trait_ranges(&fields, &mut collector);
        assert!(collector.has_errors());
    }

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

        let mut collector = ErrorCollector::new();
        validate_trait_ranges(&fields, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_valid_relationship_bond() {
        let relationship = Relationship {
            name: "Test".to_string(),
            participants: vec![],
            fields: vec![Field {
                name: "bond".to_string(),
                value: Value::Decimal(0.9),
                span: Span::new(0, 10),
            }],
            span: Span::new(0, 100),
        };

        let mut collector = ErrorCollector::new();
        validate_relationship_bonds(&[relationship], &mut collector);
        assert!(!collector.has_errors());
    }

    #[test]
    fn test_invalid_relationship_bond() {
        let relationship = Relationship {
            name: "Test".to_string(),
            participants: vec![],
            fields: vec![Field {
                name: "bond".to_string(),
                value: Value::Decimal(1.2),
                span: Span::new(0, 10),
            }],
            span: Span::new(0, 100),
        };

        let mut collector = ErrorCollector::new();
        validate_relationship_bonds(&[relationship], &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_life_arc_valid_transitions() {
        let life_arc = LifeArc {
            name: "Test".to_string(),
            required_fields: vec![],
            states: vec![
                ArcState {
                    name: "start".to_string(),
                    on_enter: None,
                    transitions: vec![Transition {
                        to: "end".to_string(),
                        condition: Expr::Identifier(vec!["ready".to_string()]),
                        span: Span::new(0, 10),
                    }],
                    span: Span::new(0, 50),
                },
                ArcState {
                    name: "end".to_string(),
                    on_enter: None,
                    transitions: vec![],
                    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());
    }

    #[test]
    fn test_life_arc_invalid_transition() {
        let life_arc = LifeArc {
            name: "Test".to_string(),
            required_fields: vec![],
            states: vec![ArcState {
                name: "start".to_string(),
                on_enter: None,
                transitions: vec![Transition {
                    to: "nonexistent".to_string(),
                    condition: Expr::Identifier(vec!["ready".to_string()]),
                    span: Span::new(0, 10),
                }],
                span: Span::new(0, 50),
            }],
            span: Span::new(0, 100),
        };

        let mut collector = ErrorCollector::new();
        validate_life_arc_transitions(&life_arc, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_behavior_tree_valid_actions() {
        let mut registry = HashSet::new();
        registry.insert("walk".to_string());
        registry.insert("eat".to_string());

        let tree = Behavior {
            name: "Test".to_string(),
            root: BehaviorNode::Sequence {
                label: None,
                children: vec![
                    BehaviorNode::Action("walk".to_string(), vec![]),
                    BehaviorNode::Action("eat".to_string(), vec![]),
                ],
            },
            span: Span::new(0, 100),
        };

        let mut collector = ErrorCollector::new();
        validate_behavior_tree_actions(&tree, &registry, &mut collector);
        assert!(!collector.has_errors());
    }

    #[test]
    fn test_behavior_tree_invalid_action() {
        // Create a registry with some actions (but not "unknown_action")
        let mut registry = HashSet::new();
        registry.insert("walk".to_string());
        registry.insert("work".to_string());

        let tree = Behavior {
            name: "Test".to_string(),
            root: BehaviorNode::Action("unknown_action".to_string(), vec![]),
            span: Span::new(0, 100),
        };

        let mut collector = ErrorCollector::new();
        validate_behavior_tree_actions(&tree, &registry, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_sub_concept_with_valid_parent() {
        let file = File {
            declarations: vec![
                Declaration::Concept(ConceptDecl {
                    name: "Cup".to_string(),
                    span: Span::new(0, 10),
                }),
                Declaration::SubConcept(SubConceptDecl {
                    name: "Type".to_string(),
                    parent_concept: "Cup".to_string(),
                    kind: SubConceptKind::Enum {
                        variants: vec![
                            "Small".to_string(),
                            "Medium".to_string(),
                            "Large".to_string(),
                        ],
                    },
                    span: Span::new(20, 60),
                }),
            ],
        };

        let mut collector = ErrorCollector::new();
        validate_sub_concept_parents(&file, &mut collector);
        assert!(!collector.has_errors());
    }

    #[test]
    fn test_sub_concept_with_missing_parent() {
        let file = File {
            declarations: vec![Declaration::SubConcept(SubConceptDecl {
                name: "Type".to_string(),
                parent_concept: "Cup".to_string(),
                kind: SubConceptKind::Enum {
                    variants: vec!["Small".to_string()],
                },
                span: Span::new(0, 40),
            })],
        };

        let mut collector = ErrorCollector::new();
        validate_sub_concept_parents(&file, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_multiple_sub_concepts_same_valid_parent() {
        let file = File {
            declarations: vec![
                Declaration::Concept(ConceptDecl {
                    name: "Cup".to_string(),
                    span: Span::new(0, 10),
                }),
                Declaration::SubConcept(SubConceptDecl {
                    name: "Type".to_string(),
                    parent_concept: "Cup".to_string(),
                    kind: SubConceptKind::Enum {
                        variants: vec!["Mug".to_string()],
                    },
                    span: Span::new(20, 40),
                }),
                Declaration::SubConcept(SubConceptDecl {
                    name: "Material".to_string(),
                    parent_concept: "Cup".to_string(),
                    kind: SubConceptKind::Enum {
                        variants: vec!["Glass".to_string()],
                    },
                    span: Span::new(50, 70),
                }),
            ],
        };

        let mut collector = ErrorCollector::new();
        validate_sub_concept_parents(&file, &mut collector);
        assert!(!collector.has_errors());
    }

    #[test]
    fn test_sub_concept_wrong_parent_name() {
        let file = File {
            declarations: vec![
                Declaration::Concept(ConceptDecl {
                    name: "Plate".to_string(),
                    span: Span::new(0, 10),
                }),
                Declaration::SubConcept(SubConceptDecl {
                    name: "Type".to_string(),
                    parent_concept: "Cup".to_string(),
                    kind: SubConceptKind::Enum {
                        variants: vec!["Small".to_string()],
                    },
                    span: Span::new(20, 40),
                }),
            ],
        };

        let mut collector = ErrorCollector::new();
        validate_sub_concept_parents(&file, &mut collector);
        assert!(collector.has_errors());
    }

    // ===== Type Invariance Tests =====

    fn make_species(name: &str, fields: Vec<Field>) -> Species {
        Species {
            name: name.to_string(),
            includes: vec![],
            fields,
            span: Span::new(0, 10),
        }
    }

    fn make_template_decl(name: &str, species_base: Option<&str>, fields: Vec<Field>) -> Template {
        Template {
            name: name.to_string(),
            species_base: species_base.map(|s| s.to_string()),
            fields,
            strict: false,
            includes: vec![],
            uses_behaviors: None,
            uses_schedule: None,
            span: Span::new(0, 10),
        }
    }

    #[test]
    fn test_type_invariance_matching_types() {
        let file = File {
            declarations: vec![
                Declaration::Species(make_species(
                    "Human",
                    vec![Field {
                        name: "age".to_string(),
                        value: Value::Number(0),
                        span: Span::new(0, 10),
                    }],
                )),
                Declaration::Template(make_template_decl(
                    "Person",
                    Some("Human"),
                    vec![Field {
                        name: "age".to_string(),
                        value: Value::Number(30),
                        span: Span::new(0, 10),
                    }],
                )),
            ],
        };

        let mut collector = ErrorCollector::new();
        validate_species_type_invariance(&file, &mut collector);
        assert!(!collector.has_errors());
    }

    #[test]
    fn test_type_invariance_mismatch() {
        let file = File {
            declarations: vec![
                Declaration::Species(make_species(
                    "Human",
                    vec![Field {
                        name: "age".to_string(),
                        value: Value::Number(0),
                        span: Span::new(0, 10),
                    }],
                )),
                Declaration::Template(make_template_decl(
                    "Person",
                    Some("Human"),
                    vec![Field {
                        name: "age".to_string(),
                        value: Value::Decimal(30.5),
                        span: Span::new(0, 10),
                    }],
                )),
            ],
        };

        let mut collector = ErrorCollector::new();
        validate_species_type_invariance(&file, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_type_invariance_number_to_text_error() {
        let file = File {
            declarations: vec![
                Declaration::Species(make_species(
                    "Human",
                    vec![Field {
                        name: "age".to_string(),
                        value: Value::Number(0),
                        span: Span::new(0, 10),
                    }],
                )),
                Declaration::Template(make_template_decl(
                    "Person",
                    Some("Human"),
                    vec![Field {
                        name: "age".to_string(),
                        value: Value::Text("old".to_string()),
                        span: Span::new(0, 10),
                    }],
                )),
            ],
        };

        let mut collector = ErrorCollector::new();
        validate_species_type_invariance(&file, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_type_invariance_range_compatible_with_number() {
        let file = File {
            declarations: vec![
                Declaration::Species(make_species(
                    "Human",
                    vec![Field {
                        name: "age".to_string(),
                        value: Value::Number(0),
                        span: Span::new(0, 10),
                    }],
                )),
                Declaration::Template(make_template_decl(
                    "Person",
                    Some("Human"),
                    vec![Field {
                        name: "age".to_string(),
                        value: Value::Range(
                            Box::new(Value::Number(18)),
                            Box::new(Value::Number(65)),
                        ),
                        span: Span::new(0, 10),
                    }],
                )),
            ],
        };

        let mut collector = ErrorCollector::new();
        validate_species_type_invariance(&file, &mut collector);
        assert!(!collector.has_errors());
    }

    #[test]
    fn test_type_invariance_no_species_base() {
        // Template without species_base should not trigger validation
        let file = File {
            declarations: vec![Declaration::Template(make_template_decl(
                "Person",
                None,
                vec![Field {
                    name: "age".to_string(),
                    value: Value::Text("old".to_string()),
                    span: Span::new(0, 10),
                }],
            ))],
        };

        let mut collector = ErrorCollector::new();
        validate_species_type_invariance(&file, &mut collector);
        assert!(!collector.has_errors());
    }

    // ===== Concept Comparison Validation Tests =====

    use crate::resolve::types::ConceptRegistry;

    fn make_definition_file() -> File {
        File {
            declarations: vec![
                Declaration::Concept(ConceptDecl {
                    name: "Cup".to_string(),
                    span: Span::new(0, 10),
                }),
                Declaration::SubConcept(SubConceptDecl {
                    name: "Type".to_string(),
                    parent_concept: "Cup".to_string(),
                    kind: SubConceptKind::Enum {
                        variants: vec![
                            "Small".to_string(),
                            "Medium".to_string(),
                            "Large".to_string(),
                        ],
                    },
                    span: Span::new(20, 60),
                }),
            ],
        }
    }

    #[test]
    fn test_definition_valid_exhaustive() {
        let mut file = make_definition_file();
        file.declarations
            .push(Declaration::Definition(DefinitionDecl {
                name: "Cup".to_string(),
                variants: vec![
                    VariantPattern {
                        name: "Small".to_string(),
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                    VariantPattern {
                        name: "Medium".to_string(),
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                    VariantPattern {
                        name: "Large".to_string(),
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                ],
                span: Span::new(0, 100),
            }));

        let registry = ConceptRegistry::from_file(&file);
        let mut collector = ErrorCollector::new();
        validate_definition_patterns(&file, &registry, &mut collector);
        assert!(!collector.has_errors());
    }

    #[test]
    fn test_definition_not_exhaustive() {
        let mut file = make_definition_file();
        file.declarations
            .push(Declaration::Definition(DefinitionDecl {
                name: "Cup".to_string(),
                variants: vec![
                    VariantPattern {
                        name: "Small".to_string(),
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                    // Missing Medium and Large
                ],
                span: Span::new(0, 100),
            }));

        let registry = ConceptRegistry::from_file(&file);
        let mut collector = ErrorCollector::new();
        validate_definition_patterns(&file, &registry, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_definition_unknown_variant() {
        let mut file = make_definition_file();
        file.declarations
            .push(Declaration::Definition(DefinitionDecl {
                name: "Cup".to_string(),
                variants: vec![
                    VariantPattern {
                        name: "Small".to_string(),
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                    VariantPattern {
                        name: "Medium".to_string(),
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                    VariantPattern {
                        name: "Large".to_string(),
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                    VariantPattern {
                        name: "Huge".to_string(), // Not a valid variant
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                ],
                span: Span::new(0, 100),
            }));

        let registry = ConceptRegistry::from_file(&file);
        let mut collector = ErrorCollector::new();
        validate_definition_patterns(&file, &registry, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_definition_missing_concept() {
        let file = File {
            declarations: vec![Declaration::Definition(DefinitionDecl {
                name: "NonExistent".to_string(),
                variants: vec![],
                span: Span::new(0, 100),
            })],
        };

        let registry = ConceptRegistry::from_file(&file);
        let mut collector = ErrorCollector::new();
        validate_definition_patterns(&file, &registry, &mut collector);
        assert!(collector.has_errors());
    }

    #[test]
    fn test_definition_with_field_conditions() {
        let mut file = make_definition_file();
        file.declarations
            .push(Declaration::Definition(DefinitionDecl {
                name: "Cup".to_string(),
                variants: vec![
                    VariantPattern {
                        name: "Small".to_string(),
                        conditions: vec![FieldCondition {
                            field_name: "material".to_string(),
                            condition: Condition::Is(vec!["Medium".to_string()]), // Valid variant ref
                            span: Span::new(0, 10),
                        }],
                        span: Span::new(0, 10),
                    },
                    VariantPattern {
                        name: "Medium".to_string(),
                        conditions: vec![FieldCondition {
                            field_name: "material".to_string(),
                            condition: Condition::Any,
                            span: Span::new(0, 10),
                        }],
                        span: Span::new(0, 10),
                    },
                    VariantPattern {
                        name: "Large".to_string(),
                        conditions: vec![],
                        span: Span::new(0, 10),
                    },
                ],
                span: Span::new(0, 100),
            }));

        let registry = ConceptRegistry::from_file(&file);
        let mut collector = ErrorCollector::new();
        validate_definition_patterns(&file, &registry, &mut collector);
        assert!(!collector.has_errors());
    }
}
