//! Phase 126: available_inputs collector (SSOT)
//!
//! ## Responsibility
//!
//! - Collect available_inputs from function params + CapturedEnv
//! - Returns BTreeMap<String, ValueId> with deterministic order
//! - No AST inference (SSOT sources only)
//!
//! ## Design
//!
//! - Input sources (priority order):
//!   1. Function params (from ScopeContext + VariableContext)
//!   2. CapturedEnv (pinned/captured variables from outer scope)
//! - Forbidden: AST-based capture inference (Phase 100 CapturedEnv is SSOT)

// Phase 126: Import contexts from MirBuilder (pub(in crate::mir) visibility)
// Since we're in crate::mir::control_tree::normalized_shadow, we can access these
use crate::mir::builder::MirBuilder;
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
use crate::mir::ValueId;
use std::collections::BTreeMap;

/// Phase 126: Box-First available_inputs collector
pub struct AvailableInputsCollectorBox;

impl AvailableInputsCollectorBox {
    /// Collect available_inputs from SSOT sources (via MirBuilder)
    ///
    /// ## Contract
    ///
    /// - Sources (priority order):
    ///   1. Function params: builder.scope_ctx.function_param_names + builder.variable_ctx.variable_map
    ///   2. CapturedEnv: captured_env.vars (pinned/captured from outer scope)
    /// - Output: BTreeMap<String, ValueId> (deterministic order)
    /// - No AST inference: only use pre-computed SSOT sources
    ///
    /// ## Implementation
    ///
    /// - Collect function params first (higher priority)
    /// - Collect CapturedEnv vars (lower priority, don't override params)
    /// - Use BTreeMap for deterministic iteration
    pub fn collect(
        builder: &MirBuilder,
        captured_env: Option<&CapturedEnv>,
    ) -> BTreeMap<String, ValueId> {
        let mut available_inputs = BTreeMap::new();

        // 1. Function params (SSOT: scope_ctx + variable_ctx)
        for param_name in &builder.scope_ctx.function_param_names {
            if let Some(value_id) = builder.variable_ctx.lookup(param_name) {
                available_inputs.insert(param_name.clone(), value_id);
            }
        }

        // 2. CapturedEnv (SSOT: pinned/captured vars)
        if let Some(env) = captured_env {
            for var in &env.vars {
                // Don't override function params (params have higher priority)
                if !available_inputs.contains_key(&var.name) {
                    available_inputs.insert(var.name.clone(), var.host_id);
                }
            }
        }

        available_inputs
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::mir::loop_pattern_detection::function_scope_capture::CapturedVar;

    #[test]
    fn test_collect_empty() {
        let builder = MirBuilder::new();
        let result = AvailableInputsCollectorBox::collect(&builder, None);
        assert_eq!(result.len(), 0);
    }

    #[test]
    fn test_collect_function_params() {
        let mut builder = MirBuilder::new();

        // Simulate function param: x -> ValueId(1)
        builder.scope_ctx.function_param_names.insert("x".to_string());
        builder.variable_ctx.insert("x".to_string(), ValueId(1));

        let result = AvailableInputsCollectorBox::collect(&builder, None);
        assert_eq!(result.len(), 1);
        assert_eq!(result.get("x"), Some(&ValueId(1)));
    }

    #[test]
    fn test_collect_captured_env() {
        let builder = MirBuilder::new();

        let mut captured = CapturedEnv::new();
        captured.insert("outer_x".to_string(), ValueId(42));

        let result = AvailableInputsCollectorBox::collect(&builder, Some(&captured));
        assert_eq!(result.len(), 1);
        assert_eq!(result.get("outer_x"), Some(&ValueId(42)));
    }

    #[test]
    fn test_collect_params_override_captured() {
        let mut builder = MirBuilder::new();

        // Function param: x -> ValueId(1)
        builder.scope_ctx.function_param_names.insert("x".to_string());
        builder.variable_ctx.insert("x".to_string(), ValueId(1));

        // Captured: x -> ValueId(42) (should be ignored)
        let mut captured = CapturedEnv::new();
        captured.insert("x".to_string(), ValueId(42));

        let result = AvailableInputsCollectorBox::collect(&builder, Some(&captured));
        assert_eq!(result.len(), 1);
        // Function param (ValueId(1)) should win over captured (ValueId(42))
        assert_eq!(result.get("x"), Some(&ValueId(1)));
    }

    #[test]
    fn test_collect_deterministic_order() {
        let mut builder = MirBuilder::new();

        // Add params in non-alphabetical order
        builder.scope_ctx.function_param_names.insert("z".to_string());
        builder.scope_ctx.function_param_names.insert("a".to_string());
        builder.scope_ctx.function_param_names.insert("m".to_string());
        builder.variable_ctx.insert("z".to_string(), ValueId(3));
        builder.variable_ctx.insert("a".to_string(), ValueId(1));
        builder.variable_ctx.insert("m".to_string(), ValueId(2));

        let result = AvailableInputsCollectorBox::collect(&builder, None);
        let keys: Vec<_> = result.keys().collect();

        // BTreeMap ensures alphabetical order
        assert_eq!(keys, vec![&"a".to_string(), &"m".to_string(), &"z".to_string()]);
    }
}
