use std::collections::{HashMap, HashSet};

use super::ast::{MachineDecl, Span, StateDecl, SystemDecl};
use super::frame_ast::{
    FrameAst, HandlerAst as FrameHandlerAst, MachineAst as FrameMachineAst, Span as FrameSpan,
    StateAst as FrameStateAst, SystemAst as FrameSystemAst, Type,
};

/// Convert a Type to its string representation — types pass through verbatim
fn type_to_string(t: &Type) -> String {
    match t {
        Type::Custom(name) => name.clone(),
        Type::Unknown => "Any".to_string(),
    }
}

/// Variable type information for Frame validation
#[derive(Debug, Clone, PartialEq)]
pub enum VarType {
    Frame,   // Frame-specific types
    Native,  // Native language types
    Unknown, // Type not yet determined
}

// ============================================================================
// Enhanced Arcanum Types (Phase 7)
// ============================================================================

/// Kind of Frame-declared symbol
#[derive(Debug, Clone, PartialEq)]
pub enum FrameSymbolKind {
    StateParam,
    HandlerParam,
    DomainVar,
    InterfaceMethod,
    Action,
    Operation,
}

/// A Frame-declared symbol with full metadata
#[derive(Debug, Clone)]
pub struct FrameSymbol {
    pub name: String,
    pub kind: FrameSymbolKind,
    pub declared_at: Span,
    pub symbol_type: Option<String>,   // Type annotation if present
    pub default_value: Option<String>, // Default for enter/exit handler params
}

/// Handler entry with parameters and body span
#[derive(Debug, Clone)]
pub struct HandlerEntry {
    pub event: String,
    pub params: Vec<FrameSymbol>,
    pub return_type: Option<String>, // Return type annotation if present
    pub return_init: Option<String>, // Default return value: = @@:(expr)
    pub body_span: Span,             // For splicer to extract body content (Path B)
    pub body_statements: Vec<crate::frame_c::compiler::frame_ast::Statement>, // AST body for codegen (Path A)
    pub is_enter: bool,                                                       // $>
    pub is_exit: bool,                                                        // $<
    /// Source comments preceding the handler declaration. Threaded
    /// through from `HandlerAst.leading_comments` /
    /// `EnterHandler.leading_comments` / `ExitHandler.leading_comments`
    /// by `build_handler_entry_from_ast` and the enter/exit branches.
    /// Codegen prepends them as NativeBlock nodes before the
    /// per-handler method emission.
    pub leading_comments: Vec<String>,
}

/// Enhanced state entry with handlers
#[derive(Debug, Clone)]
pub struct EnhancedStateEntry {
    pub name: String,
    pub params: Vec<FrameSymbol>,
    pub parent: Option<String>,
    pub handlers: HashMap<String, HandlerEntry>,
    pub span: Span,
}

/// Enhanced machine entry with enhanced states
#[derive(Debug, Clone, Default)]
pub struct EnhancedMachineEntry {
    pub states: HashMap<String, EnhancedStateEntry>,
}

// ============================================================================
// Original Arcanum Types (kept for backwards compatibility)
// ============================================================================

/// Enhanced Arcanum - Frame's authoritative symbol table for validation
#[derive(Debug, Default, Clone)]
pub struct Arcanum {
    pub systems: HashMap<String, SystemEntry>,
}

/// Complete system metadata for validation
#[derive(Debug, Default, Clone)]
pub struct SystemEntry {
    pub decl: Option<SystemDecl>,
    pub machines: HashMap<String, MachineEntry>,

    // Enhanced for validation (Stage 1)
    pub interface_methods: HashSet<String>,
    pub actions: HashSet<String>,
    pub operations: HashSet<String>,
    pub domain_vars: HashMap<String, VarType>,

    // Phase 7: Enhanced with full symbol info
    pub domain_symbols: HashMap<String, FrameSymbol>,
    pub enhanced_machines: HashMap<String, EnhancedMachineEntry>,

    /// True if any interface method is declared async — triggers async dispatch chain
    pub needs_async: bool,
}

#[derive(Debug, Default, Clone)]
pub struct MachineEntry {
    pub decl: Option<MachineDecl>,
    pub states: HashMap<String, StateDecl>,
}

impl Arcanum {
    pub fn new() -> Self {
        Self {
            systems: HashMap::new(),
        }
    }

    pub fn resolve_state<'a>(&'a self, system: &str, state: &str) -> Option<&'a StateDecl> {
        self.systems
            .get(system)
            .and_then(|s| s.machines.values().find_map(|m| m.states.get(state)))
    }

    pub fn has_parent(&self, system: &str, state: &str) -> bool {
        self.resolve_state(system, state)
            .and_then(|d| d.parent.as_ref())
            .is_some()
    }

    pub fn any_parent_relation(&self) -> bool {
        self.systems.values().any(|sys| {
            sys.machines
                .values()
                .any(|m| m.states.values().any(|s| s.parent.is_some()))
        })
    }

    pub fn all_state_names(&self) -> std::collections::HashSet<String> {
        let mut set = std::collections::HashSet::new();
        for sys in self.systems.values() {
            for m in sys.machines.values() {
                for s in m.states.values() {
                    set.insert(s.name.clone());
                }
            }
        }
        set
    }

    // Enhanced validation methods (Stage 1)

    /// Check if a method is in the system's interface (E406)
    pub fn is_interface_method(&self, system: &str, method: &str) -> bool {
        self.systems
            .get(system)
            .map(|s| s.interface_methods.contains(method))
            .unwrap_or(false)
    }

    /// Check if an action exists in the system
    pub fn has_action(&self, system: &str, action: &str) -> bool {
        self.systems
            .get(system)
            .map(|s| s.actions.contains(action))
            .unwrap_or(false)
    }

    /// Check if an operation exists in the system
    pub fn has_operation(&self, system: &str, operation: &str) -> bool {
        self.systems
            .get(system)
            .map(|s| s.operations.contains(operation))
            .unwrap_or(false)
    }

    /// Get state parameter count for arity validation (E405)
    pub fn get_state_param_count(&self, system: &str, state: &str) -> Option<usize> {
        self.resolve_state(system, state).map(|s| s.params.len())
    }

    /// Get enter handler parameter count for transition arg validation (E417)
    /// Enter args (-> (args) $State) go to the enter handler ($>)
    pub fn get_enter_handler_param_count(&self, system: &str, state: &str) -> Option<usize> {
        self.get_enhanced_state(system, state)
            .and_then(|s| s.handlers.get("$>"))
            .map(|h| h.params.len())
    }

    /// Get exit handler parameter count for transition arg validation (E419)
    /// Exit args ((args) -> $State) go to the exit handler ($<)
    pub fn get_exit_handler_param_count(&self, system: &str, state: &str) -> Option<usize> {
        self.get_enhanced_state(system, state)
            .and_then(|s| s.handlers.get("$<"))
            .map(|h| h.params.len())
    }

    /// Check if state has an enter handler
    pub fn has_enter_handler(&self, system: &str, state: &str) -> bool {
        self.get_enhanced_state(system, state)
            .map(|s| s.handlers.contains_key("$>"))
            .unwrap_or(false)
    }

    /// Check if state has an exit handler
    pub fn has_exit_handler(&self, system: &str, state: &str) -> bool {
        self.get_enhanced_state(system, state)
            .map(|s| s.handlers.contains_key("$<"))
            .unwrap_or(false)
    }

    /// Validate state transition exists (E402)
    pub fn validate_transition(&self, system: &str, target_state: &str) -> Result<(), String> {
        if self.resolve_state(system, target_state).is_some() {
            Ok(())
        } else {
            let available = self.get_system_states(system);
            if available.is_empty() {
                Err(format!(
                    "Unknown state '{}' (system has no states)",
                    target_state
                ))
            } else {
                Err(format!(
                    "Unknown state '{}'. Available states: {}",
                    target_state,
                    available.join(", ")
                ))
            }
        }
    }

    /// Get all states in a system (for error messages)
    pub fn get_system_states(&self, system: &str) -> Vec<String> {
        self.systems
            .get(system)
            .map(|s| {
                let mut states = Vec::new();
                for m in s.machines.values() {
                    for state in m.states.keys() {
                        states.push(format!("${}", state));
                    }
                }
                states.sort();
                states
            })
            .unwrap_or_default()
    }

    // ========================================================================
    // Phase 7: Enhanced Scope Resolution API
    // ========================================================================

    /// Resolve a Frame symbol in the given scope context.
    /// Searches handler scope → state scope → system scope.
    /// Returns None if not a Frame symbol (might be native variable).
    pub fn resolve_frame_symbol(
        &self,
        system: &str,
        state: Option<&str>,
        handler: Option<&str>,
        name: &str,
    ) -> Option<&FrameSymbol> {
        let sys = self.systems.get(system)?;

        // 1. Check handler scope (if in a handler)
        if let (Some(state_name), Some(handler_name)) = (state, handler) {
            if let Some(machine) = sys.enhanced_machines.values().next() {
                if let Some(state_entry) = machine.states.get(state_name) {
                    if let Some(handler_entry) = state_entry.handlers.get(handler_name) {
                        if let Some(sym) = handler_entry.params.iter().find(|p| p.name == name) {
                            return Some(sym);
                        }
                    }
                }
            }
        }

        // 2. Check state scope (if in a state)
        if let Some(state_name) = state {
            if let Some(machine) = sys.enhanced_machines.values().next() {
                if let Some(state_entry) = machine.states.get(state_name) {
                    if let Some(sym) = state_entry.params.iter().find(|p| p.name == name) {
                        return Some(sym);
                    }
                }
            }
        }

        // 3. Check system scope (domain vars)
        if let Some(sym) = sys.domain_symbols.get(name) {
            return Some(sym);
        }

        // Not a Frame symbol
        None
    }

    /// Check if a name is a Frame-declared symbol in the given context
    pub fn is_frame_symbol(
        &self,
        system: &str,
        state: Option<&str>,
        handler: Option<&str>,
        name: &str,
    ) -> bool {
        self.resolve_frame_symbol(system, state, handler, name)
            .is_some()
    }

    /// Get all handlers for a state (for codegen iteration)
    pub fn get_state_handlers(&self, system: &str, state: &str) -> Vec<&HandlerEntry> {
        self.systems
            .get(system)
            .and_then(|s| s.enhanced_machines.values().next())
            .and_then(|m| m.states.get(state))
            .map(|s| s.handlers.values().collect())
            .unwrap_or_default()
    }

    /// Get enhanced state entry (for codegen)
    pub fn get_enhanced_state(&self, system: &str, state: &str) -> Option<&EnhancedStateEntry> {
        self.systems
            .get(system)
            .and_then(|s| s.enhanced_machines.values().next())
            .and_then(|m| m.states.get(state))
    }

    /// Get all enhanced states for a system (for codegen iteration)
    pub fn get_enhanced_states(&self, system: &str) -> Vec<&EnhancedStateEntry> {
        self.systems
            .get(system)
            .and_then(|s| s.enhanced_machines.values().next())
            .map(|m| m.states.values().collect())
            .unwrap_or_default()
    }
}
/// Build Arcanum from the proper Frame AST (replaces byte-scanning approach)
///
/// This is the preferred way to build the symbol table. It extracts all
/// information directly from the parsed Frame AST, avoiding the error-prone
/// byte-level scanning.
pub fn build_arcanum_from_frame_ast(ast: &FrameAst) -> Arcanum {
    let mut arc = Arcanum::new();

    match ast {
        FrameAst::System(system) => {
            let entry = build_system_entry_from_frame_ast(system);
            arc.systems.insert(system.name.clone(), entry);
        }
        FrameAst::Module(module) => {
            for system in &module.systems {
                let entry = build_system_entry_from_frame_ast(system);
                arc.systems.insert(system.name.clone(), entry);
            }
        }
    }

    if std::env::var("FRAME_TRANSPILER_DEBUG").ok().as_deref() == Some("1") {
        eprintln!(
            "[build_arcanum_from_frame_ast] Built arcanum with {} systems",
            arc.systems.len()
        );
        for (name, entry) in &arc.systems {
            eprintln!("[build_arcanum_from_frame_ast]   System '{}': {} states, {} interface, {} actions, {} operations",
                name,
                entry.machines.values().map(|m| m.states.len()).sum::<usize>(),
                entry.interface_methods.len(),
                entry.actions.len(),
                entry.operations.len()
            );
        }
    }

    arc
}

/// Build a SystemEntry from a Frame AST SystemAst
fn build_system_entry_from_frame_ast(system: &FrameSystemAst) -> SystemEntry {
    let mut entry = SystemEntry::default();

    // Extract interface methods and check for async
    for method in &system.interface {
        entry.interface_methods.insert(method.name.clone());
        if method.is_async {
            entry.needs_async = true;
        }
    }

    // Extract actions
    for action in &system.actions {
        entry.actions.insert(action.name.clone());
    }

    // Extract operations
    for operation in &system.operations {
        entry.operations.insert(operation.name.clone());
    }

    // Extract domain variables
    for var in &system.domain {
        entry.domain_vars.insert(var.name.clone(), VarType::Native);
    }

    // Phase 7: Extract domain variables with full symbol info
    for var in &system.domain {
        let symbol = FrameSymbol {
            name: var.name.clone(),
            kind: FrameSymbolKind::DomainVar,
            declared_at: Span {
                start: var.span.start,
                end: var.span.end,
            },
            symbol_type: Some(format!("{:?}", var.var_type)),
            default_value: None,
        };
        entry.domain_symbols.insert(var.name.clone(), symbol);
    }

    // Extract machine states (old style for backwards compat)
    if let Some(ref machine) = system.machine {
        let mut machine_entry = MachineEntry::default();

        for state in &machine.states {
            let state_decl = build_state_decl_from_frame_ast(state);
            machine_entry.states.insert(state.name.clone(), state_decl);
        }

        entry.machines.insert("machine".to_string(), machine_entry);
    }

    // Phase 7: Build enhanced machine with handlers
    if let Some(ref machine) = system.machine {
        let enhanced_machine = build_enhanced_machine_from_frame_ast(machine);
        entry
            .enhanced_machines
            .insert("machine".to_string(), enhanced_machine);
    }

    entry
}

/// Build an EnhancedMachineEntry from Frame AST MachineAst
fn build_enhanced_machine_from_frame_ast(machine: &FrameMachineAst) -> EnhancedMachineEntry {
    let mut entry = EnhancedMachineEntry::default();

    for state in &machine.states {
        let enhanced_state = build_enhanced_state_from_frame_ast(state);
        entry.states.insert(state.name.clone(), enhanced_state);
    }

    entry
}

/// Build an EnhancedStateEntry from Frame AST StateAst
fn build_enhanced_state_from_frame_ast(state: &FrameStateAst) -> EnhancedStateEntry {
    let mut handlers = HashMap::new();

    // Helper to convert frame_ast::Span to ast::Span
    fn convert_span(s: &FrameSpan) -> Span {
        Span {
            start: s.start,
            end: s.end,
        }
    }

    // Convert state parameters to FrameSymbols
    let params: Vec<FrameSymbol> = state
        .params
        .iter()
        .map(|p| FrameSymbol {
            name: p.name.clone(),
            kind: FrameSymbolKind::StateParam,
            declared_at: convert_span(&p.span),
            symbol_type: Some(type_to_string(&p.param_type)),
            default_value: None,
        })
        .collect();

    // Convert regular handlers
    for handler in &state.handlers {
        let handler_entry = build_handler_entry_from_ast(handler);
        handlers.insert(handler.event.clone(), handler_entry);
    }

    // Convert enter handler ($>)
    if let Some(ref enter) = state.enter {
        let handler_entry = HandlerEntry {
            event: "$>".to_string(),
            params: enter
                .params
                .iter()
                .map(|p| FrameSymbol {
                    name: p.name.clone(),
                    kind: FrameSymbolKind::HandlerParam,
                    declared_at: convert_span(&p.span),
                    symbol_type: Some(type_to_string(&p.param_type)),
                    default_value: p.default_value.clone(),
                })
                .collect(),
            return_type: None, // Enter handlers don't have return types
            return_init: None,
            body_span: convert_span(&enter.body.span),
            body_statements: enter.body.statements.clone(),
            is_enter: true,
            is_exit: false,
            leading_comments: enter.leading_comments.clone(),
        };
        handlers.insert("$>".to_string(), handler_entry);
    }

    // Convert exit handler ($<)
    if let Some(ref exit) = state.exit {
        let handler_entry = HandlerEntry {
            event: "$<".to_string(),
            params: exit
                .params
                .iter()
                .map(|p| FrameSymbol {
                    name: p.name.clone(),
                    kind: FrameSymbolKind::HandlerParam,
                    declared_at: convert_span(&p.span),
                    symbol_type: Some(type_to_string(&p.param_type)),
                    default_value: p.default_value.clone(),
                })
                .collect(),
            return_type: None, // Exit handlers don't have return types
            return_init: None,
            body_span: convert_span(&exit.body.span),
            body_statements: exit.body.statements.clone(),
            is_enter: false,
            is_exit: true,
            leading_comments: exit.leading_comments.clone(),
        };
        handlers.insert("$<".to_string(), handler_entry);
    }

    EnhancedStateEntry {
        name: state.name.clone(),
        params,
        parent: state.parent.clone(),
        handlers,
        span: convert_span(&state.span),
    }
}

/// Build a HandlerEntry from Frame AST HandlerAst
fn build_handler_entry_from_ast(handler: &FrameHandlerAst) -> HandlerEntry {
    // Helper to convert frame_ast::Span to ast::Span
    fn convert_span(s: &FrameSpan) -> Span {
        Span {
            start: s.start,
            end: s.end,
        }
    }

    let params: Vec<FrameSymbol> = handler
        .params
        .iter()
        .map(|p| FrameSymbol {
            name: p.name.clone(),
            kind: FrameSymbolKind::HandlerParam,
            declared_at: convert_span(&p.span),
            symbol_type: Some(type_to_string(&p.param_type)),
            default_value: None,
        })
        .collect();

    HandlerEntry {
        event: handler.event.clone(),
        params,
        return_type: handler.return_type.as_ref().map(type_to_string),
        return_init: handler.return_init.clone(),
        body_span: convert_span(&handler.body.span),
        body_statements: handler.body.statements.clone(),
        is_enter: false,
        is_exit: false,
        leading_comments: handler.leading_comments.clone(),
    }
}

/// Build a StateDecl from a Frame AST StateAst
fn build_state_decl_from_frame_ast(state: &FrameStateAst) -> StateDecl {
    StateDecl {
        name: state.name.clone(),
        parent: state.parent.clone(),
        params: state.params.iter().map(|p| p.name.clone()).collect(),
        span: Span {
            start: state.span.start,
            end: state.span.end,
        },
    }
}
