//! Resolved types - what consumers see after resolution
//!
//! These types are similar to AST types but represent fully resolved,
//! validated entities with all cross-references resolved.

use std::collections::HashMap;

use crate::{
    resolve::{
        ResolveError,
        Result,
    },
    syntax::ast::{
        Declaration,
        Field,
        File,
        Span,
        SubConceptKind,
    },
};

/// A fully resolved file with all cross-references resolved
#[derive(Debug, Clone)]
pub struct ResolvedFile {
    pub declarations: Vec<ResolvedDeclaration>,
}

/// A resolved top-level declaration
#[derive(Debug, Clone)]
pub enum ResolvedDeclaration {
    Character(ResolvedCharacter),
    Template(ResolvedTemplate),
    LifeArc(ResolvedLifeArc),
    Schedule(ResolvedSchedule),
    Behavior(ResolvedBehavior),
    Institution(ResolvedInstitution),
    Relationship(ResolvedRelationship),
    Location(ResolvedLocation),
    Species(ResolvedSpecies),
    Enum(ResolvedEnum),
}

/// A resolved character with all template overrides applied
#[derive(Debug, Clone)]
pub struct ResolvedCharacter {
    pub name: String,
    pub species: Option<String>,
    pub fields: Vec<Field>,
    pub span: Span,
    /// Qualified path (e.g., characters::Martha)
    pub qualified_path: Vec<String>,
}

/// A resolved template definition
#[derive(Debug, Clone)]
pub struct ResolvedTemplate {
    pub name: String,
    pub fields: Vec<Field>,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

/// A resolved life arc state machine
#[derive(Debug, Clone)]
pub struct ResolvedLifeArc {
    pub name: String,
    pub states: Vec<ResolvedArcState>,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

#[derive(Debug, Clone)]
pub struct ResolvedArcState {
    pub name: String,
    pub transitions: Vec<ResolvedTransition>,
    pub span: Span,
}

#[derive(Debug, Clone)]
pub struct ResolvedTransition {
    pub to: String,
    /// Validated - target state exists
    pub target_state_index: usize,
    pub condition: crate::syntax::ast::Expr,
    pub span: Span,
}

/// A resolved schedule
#[derive(Debug, Clone)]
pub struct ResolvedSchedule {
    pub name: String,
    pub blocks: Vec<crate::syntax::ast::ScheduleBlock>,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

/// A resolved behavior tree
#[derive(Debug, Clone)]
pub struct ResolvedBehavior {
    pub name: String,
    pub root: crate::syntax::ast::BehaviorNode,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

/// A resolved institution
#[derive(Debug, Clone)]
pub struct ResolvedInstitution {
    pub name: String,
    pub fields: Vec<Field>,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

/// A resolved relationship with both sides validated
#[derive(Debug, Clone)]
pub struct ResolvedRelationship {
    pub name: String,
    pub participants: Vec<crate::syntax::ast::Participant>,
    pub fields: Vec<Field>,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

/// A resolved location
#[derive(Debug, Clone)]
pub struct ResolvedLocation {
    pub name: String,
    pub fields: Vec<Field>,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

/// A resolved species
#[derive(Debug, Clone)]
pub struct ResolvedSpecies {
    pub name: String,
    pub fields: Vec<Field>,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

/// A resolved enum
#[derive(Debug, Clone)]
pub struct ResolvedEnum {
    pub name: String,
    pub variants: Vec<String>,
    pub span: Span,
    pub qualified_path: Vec<String>,
}

// ===== Concept Registry =====

/// Information about a registered concept
#[derive(Debug, Clone)]
pub struct ConceptInfo {
    pub name: String,
    pub sub_concepts: HashMap<String, SubConceptInfo>,
}

/// Information about a registered sub-concept
#[derive(Debug, Clone)]
pub struct SubConceptInfo {
    pub name: String,
    pub parent: String,
    pub kind: SubConceptKind,
}

/// Registry tracking all concepts and sub_concepts for type checking
#[derive(Debug, Clone, Default)]
pub struct ConceptRegistry {
    concepts: HashMap<String, ConceptInfo>,
}

impl ConceptRegistry {
    pub fn new() -> Self {
        Self {
            concepts: HashMap::new(),
        }
    }

    /// Build a registry from a parsed file
    pub fn from_file(file: &File) -> Self {
        let mut registry = Self::new();

        for decl in &file.declarations {
            match decl {
                | Declaration::Concept(c) => {
                    registry.register_concept(c.name.clone());
                },
                | Declaration::SubConcept(sc) => {
                    registry.register_sub_concept(
                        sc.parent_concept.clone(),
                        sc.name.clone(),
                        sc.kind.clone(),
                    );
                },
                | _ => {},
            }
        }

        registry
    }

    /// Register a new concept
    pub fn register_concept(&mut self, name: String) {
        self.concepts
            .entry(name.clone())
            .or_insert_with(|| ConceptInfo {
                name,
                sub_concepts: HashMap::new(),
            });
    }

    /// Register a sub-concept under its parent
    pub fn register_sub_concept(&mut self, parent: String, name: String, kind: SubConceptKind) {
        let concept = self
            .concepts
            .entry(parent.clone())
            .or_insert_with(|| ConceptInfo {
                name: parent.clone(),
                sub_concepts: HashMap::new(),
            });

        concept
            .sub_concepts
            .insert(name.clone(), SubConceptInfo { name, parent, kind });
    }

    /// Look up a concept by name
    pub fn lookup_concept(&self, name: &str) -> Option<&ConceptInfo> {
        self.concepts.get(name)
    }

    /// Validate that a sub-concept variant reference is valid
    ///
    /// For enum sub-concepts, checks that the variant exists.
    /// For record sub-concepts, checks that the field exists.
    pub fn validate_sub_concept_reference(
        &self,
        parent: &str,
        sub_concept: &str,
        variant: &str,
    ) -> Result<()> {
        let concept = self
            .concepts
            .get(parent)
            .ok_or_else(|| ResolveError::ValidationError {
                message: format!("Concept '{}' not found", parent),
                help: Some(format!(
                    "Add 'concept {}' to define this concept before referencing it.",
                    parent
                )),
            })?;

        let sc = concept.sub_concepts.get(sub_concept).ok_or_else(|| {
            let available = concept
                .sub_concepts
                .keys()
                .map(|k| k.as_str())
                .collect::<Vec<_>>()
                .join(", ");
            ResolveError::ValidationError {
                message: format!("Sub-concept '{}.{}' not found", parent, sub_concept),
                help: Some(format!(
                    "Available sub-concepts for '{}': {}",
                    parent,
                    if available.is_empty() {
                        "(none)".to_string()
                    } else {
                        available
                    }
                )),
            }
        })?;

        match &sc.kind {
            | SubConceptKind::Enum { variants } => {
                if !variants.contains(&variant.to_string()) {
                    let available = variants.join(", ");
                    return Err(ResolveError::ValidationError {
                        message: format!(
                            "Variant '{}' not found in sub-concept '{}.{}'",
                            variant, parent, sub_concept
                        ),
                        help: Some(format!("Available variants: {}", available)),
                    });
                }
            },
            | SubConceptKind::Record { fields } => {
                if !fields.iter().any(|f| f.name == variant) {
                    let available = fields
                        .iter()
                        .map(|f| f.name.as_str())
                        .collect::<Vec<_>>()
                        .join(", ");
                    return Err(ResolveError::ValidationError {
                        message: format!(
                            "Field '{}' not found in record sub-concept '{}.{}'",
                            variant, parent, sub_concept
                        ),
                        help: Some(format!("Available fields: {}", available)),
                    });
                }
            },
        }

        Ok(())
    }
}

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

    #[test]
    fn test_register_and_lookup_concept() {
        let mut registry = ConceptRegistry::new();
        registry.register_concept("Cup".to_string());

        let concept = registry.lookup_concept("Cup");
        assert!(concept.is_some());
        assert_eq!(concept.unwrap().name, "Cup");
    }

    #[test]
    fn test_lookup_missing_concept() {
        let registry = ConceptRegistry::new();
        assert!(registry.lookup_concept("Cup").is_none());
    }

    #[test]
    fn test_register_sub_concept_enum() {
        let mut registry = ConceptRegistry::new();
        registry.register_concept("Cup".to_string());
        registry.register_sub_concept(
            "Cup".to_string(),
            "Type".to_string(),
            SubConceptKind::Enum {
                variants: vec![
                    "Small".to_string(),
                    "Medium".to_string(),
                    "Large".to_string(),
                ],
            },
        );

        let concept = registry.lookup_concept("Cup").unwrap();
        assert_eq!(concept.sub_concepts.len(), 1);
        assert!(concept.sub_concepts.contains_key("Type"));
    }

    #[test]
    fn test_register_sub_concept_record() {
        let mut registry = ConceptRegistry::new();
        registry.register_concept("Cup".to_string());
        registry.register_sub_concept(
            "Cup".to_string(),
            "Material".to_string(),
            SubConceptKind::Record {
                fields: vec![Field {
                    name: "weight".to_string(),
                    value: crate::syntax::ast::Value::Number(100),
                    span: Span::new(0, 10),
                }],
            },
        );

        let concept = registry.lookup_concept("Cup").unwrap();
        assert!(concept.sub_concepts.contains_key("Material"));
    }

    #[test]
    fn test_validate_valid_variant_reference() {
        let mut registry = ConceptRegistry::new();
        registry.register_concept("Cup".to_string());
        registry.register_sub_concept(
            "Cup".to_string(),
            "Type".to_string(),
            SubConceptKind::Enum {
                variants: vec!["Small".to_string(), "Large".to_string()],
            },
        );

        assert!(registry
            .validate_sub_concept_reference("Cup", "Type", "Small")
            .is_ok());
    }

    #[test]
    fn test_validate_invalid_variant_reference() {
        let mut registry = ConceptRegistry::new();
        registry.register_concept("Cup".to_string());
        registry.register_sub_concept(
            "Cup".to_string(),
            "Type".to_string(),
            SubConceptKind::Enum {
                variants: vec!["Small".to_string(), "Large".to_string()],
            },
        );

        let result = registry.validate_sub_concept_reference("Cup", "Type", "Huge");
        assert!(result.is_err());
    }

    #[test]
    fn test_validate_missing_concept() {
        let registry = ConceptRegistry::new();
        let result = registry.validate_sub_concept_reference("Cup", "Type", "Small");
        assert!(result.is_err());
    }

    #[test]
    fn test_validate_missing_sub_concept() {
        let mut registry = ConceptRegistry::new();
        registry.register_concept("Cup".to_string());

        let result = registry.validate_sub_concept_reference("Cup", "Type", "Small");
        assert!(result.is_err());
    }

    #[test]
    fn test_validate_record_field_reference() {
        let mut registry = ConceptRegistry::new();
        registry.register_concept("Cup".to_string());
        registry.register_sub_concept(
            "Cup".to_string(),
            "Size".to_string(),
            SubConceptKind::Record {
                fields: vec![
                    Field {
                        name: "height".to_string(),
                        value: crate::syntax::ast::Value::Number(10),
                        span: Span::new(0, 10),
                    },
                    Field {
                        name: "width".to_string(),
                        value: crate::syntax::ast::Value::Number(5),
                        span: Span::new(0, 10),
                    },
                ],
            },
        );

        assert!(registry
            .validate_sub_concept_reference("Cup", "Size", "height")
            .is_ok());
        assert!(registry
            .validate_sub_concept_reference("Cup", "Size", "depth")
            .is_err());
    }

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

        let registry = ConceptRegistry::from_file(&file);
        let concept = registry.lookup_concept("Cup").unwrap();
        assert_eq!(concept.sub_concepts.len(), 1);
        assert!(registry
            .validate_sub_concept_reference("Cup", "Type", "Small")
            .is_ok());
    }

    #[test]
    fn test_sub_concept_auto_creates_parent() {
        let mut registry = ConceptRegistry::new();
        // Register sub-concept without explicitly registering parent
        registry.register_sub_concept(
            "Cup".to_string(),
            "Type".to_string(),
            SubConceptKind::Enum {
                variants: vec!["Small".to_string()],
            },
        );

        // Parent should be auto-created
        let concept = registry.lookup_concept("Cup");
        assert!(concept.is_some());
        assert_eq!(concept.unwrap().sub_concepts.len(), 1);
    }
}
