//! NormalizationExecuteBox: Execute normalization plan (Phase 134 P0)
//!
//! ## Responsibility
//!
//! - Execute a NormalizationPlan from PlanBox
//! - Build StepTree, lower to JoinIR, merge into MIR
//! - SSOT for "how to execute" normalization
//!
//! ## Contract
//!
//! - Modifies builder state (adds blocks, instructions, updates variable_map)
//! - Uses DirectValue mode (no PHI generation)
//! - Returns Ok(()) on success, Err(_) on failure

use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
use super::plan::{NormalizationPlan, PlanKind};

/// Box-First: Execute normalization plan
pub struct NormalizationExecuteBox;

impl NormalizationExecuteBox {
    /// Execute a normalization plan
    ///
    /// Returns:
    /// - Ok(value_id): Successfully executed, returns result value
    /// - Err(_): Lowering or merge failed
    pub fn execute(
        builder: &mut MirBuilder,
        plan: &NormalizationPlan,
        remaining: &[ASTNode],
        func_name: &str,
        debug: bool,
    ) -> Result<ValueId, String> {
        let trace = crate::mir::builder::control_flow::joinir::trace::trace();

        if debug {
            trace.routing(
                "normalization/execute",
                func_name,
                &format!(
                    "Executing plan: kind={:?}, consumed={}",
                    plan.kind, plan.consumed
                ),
            );
        }

        // Validate consumed vs remaining
        if plan.consumed > remaining.len() {
            return Err(format!(
                "[normalization/execute] Plan wants to consume {} statements but only {} available",
                plan.consumed,
                remaining.len()
            ));
        }

        match &plan.kind {
            PlanKind::LoopOnly => {
                Self::execute_loop_only(builder, remaining, func_name, debug)
            }
            PlanKind::LoopWithPost { .. } => {
                Self::execute_loop_with_post(builder, plan, remaining, func_name, debug)
            }
        }
    }

    /// Execute Phase 131: Loop-only pattern
    fn execute_loop_only(
        builder: &mut MirBuilder,
        remaining: &[ASTNode],
        func_name: &str,
        debug: bool,
    ) -> Result<ValueId, String> {
        use crate::ast::Span;
        use crate::mir::control_tree::normalized_shadow::available_inputs_collector::AvailableInputsCollectorBox;
        use crate::mir::control_tree::normalized_shadow::StepTreeNormalizedShadowLowererBox;
        use crate::mir::control_tree::StepTreeBuilderBox;

        let trace = crate::mir::builder::control_flow::joinir::trace::trace();

        // Build StepTree from loop AST
        let loop_ast = if let ASTNode::Loop { condition, body, .. } = &remaining[0] {
            ASTNode::Loop {
                condition: condition.clone(),
                body: body.clone(),
                span: Span::unknown(),
            }
        } else {
            return Err("[normalization/execute] First statement is not a loop".to_string());
        };

        let tree = StepTreeBuilderBox::build_from_ast(&loop_ast);

        // Collect available inputs
        let available_inputs = AvailableInputsCollectorBox::collect(builder, None);

        if debug {
            trace.routing(
                "normalization/execute/loop_only",
                func_name,
                &format!("Available inputs: {}", available_inputs.len()),
            );
        }

        // Try Normalized lowering
        let (join_module, join_meta) =
            match StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree, &available_inputs) {
                Ok(Some(result)) => result,
                Ok(None) => {
                    return Err(
                        "[normalization/execute] StepTree lowering returned None (out of scope)"
                            .to_string(),
                    );
                }
                Err(e) => {
                    if crate::config::env::joinir_dev::strict_enabled() {
                        use crate::mir::join_ir::lowering::error_tags;
                        return Err(error_tags::freeze_with_hint(
                            "phase134/normalization/loop_only",
                            &e,
                            "Loop should be supported by Normalized but lowering failed",
                        ));
                    }
                    return Err(format!("[normalization/execute] Lowering failed: {}", e));
                }
            };

        // Merge JoinIR into MIR
        Self::merge_normalized_joinir(builder, join_module, join_meta, func_name, debug)?;

        // Return void constant (loop doesn't produce a value)
        use crate::mir::{ConstValue, MirInstruction};
        let void_id = builder.next_value_id();
        builder.emit_instruction(MirInstruction::Const {
            dst: void_id,
            value: ConstValue::Void,
        })?;

        Ok(void_id)
    }

    /// Execute Phase 132-133: Loop + post assignments + return
    fn execute_loop_with_post(
        builder: &mut MirBuilder,
        plan: &NormalizationPlan,
        remaining: &[ASTNode],
        func_name: &str,
        debug: bool,
    ) -> Result<ValueId, String> {
        use crate::mir::control_tree::normalized_shadow::available_inputs_collector::AvailableInputsCollectorBox;
        use crate::mir::control_tree::normalized_shadow::StepTreeNormalizedShadowLowererBox;
        use crate::mir::control_tree::StepTreeBuilderBox;

        let trace = crate::mir::builder::control_flow::joinir::trace::trace();

        // Build StepTree from the suffix (loop + assigns + return)
        let suffix = &remaining[..plan.consumed];
        let step_tree = StepTreeBuilderBox::build_from_block(suffix);

        // Collect available inputs
        let available_inputs = AvailableInputsCollectorBox::collect(builder, None);

        if debug {
            trace.routing(
                "normalization/execute/loop_with_post",
                func_name,
                &format!(
                    "Suffix: {} statements, available inputs: {}",
                    suffix.len(),
                    available_inputs.len()
                ),
            );
        }

        // Try Normalized lowering
        let (join_module, join_meta) =
            match StepTreeNormalizedShadowLowererBox::try_lower_if_only(&step_tree, &available_inputs) {
                Ok(Some(result)) => result,
                Ok(None) => {
                    return Err(
                        "[normalization/execute] StepTree lowering returned None (out of scope)"
                            .to_string(),
                    );
                }
                Err(e) => {
                    if crate::config::env::joinir_dev::strict_enabled() {
                        use crate::mir::join_ir::lowering::error_tags;
                        return Err(error_tags::freeze_with_hint(
                            "phase134/normalization/loop_with_post",
                            &e,
                            "Loop+post should be supported by Normalized but lowering failed",
                        ));
                    }
                    return Err(format!("[normalization/execute] Lowering failed: {}", e));
                }
            };

        // Merge JoinIR into MIR
        Self::merge_normalized_joinir(builder, join_module, join_meta, func_name, debug)?;

        // For suffix patterns, emit the return statement
        // (JoinIR merge converts fragment Returns to Jump, so we need host-level return)
        if plan.requires_return {
            if let Some(ASTNode::Return { value, .. }) = suffix.last() {
                let _ = builder.build_return_statement(value.clone())?;
            }
        }

        // Return void constant
        use crate::mir::{ConstValue, MirInstruction};
        let void_id = builder.next_value_id();
        builder.emit_instruction(MirInstruction::Const {
            dst: void_id,
            value: ConstValue::Void,
        })?;

        Ok(void_id)
    }

    /// Merge Normalized JoinModule into MIR builder
    ///
    /// Extracted from routing.rs and suffix_router_box.rs
    fn merge_normalized_joinir(
        builder: &mut MirBuilder,
        join_module: crate::mir::join_ir::JoinModule,
        join_meta: crate::mir::join_ir::lowering::carrier_info::JoinFragmentMeta,
        func_name: &str,
        debug: bool,
    ) -> Result<(), String> {
        use crate::mir::builder::control_flow::joinir::merge;
        use crate::mir::join_ir::frontend::JoinFuncMetaMap;
        use crate::mir::join_ir::lowering::carrier_info::{CarrierRole, ExitReconnectMode};
        use crate::mir::join_ir::lowering::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
        use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
        use std::collections::BTreeMap;

        let trace = crate::mir::builder::control_flow::joinir::trace::trace();

        // Build exit_bindings from meta
        let exit_bindings: Vec<LoopExitBinding> = join_meta
            .exit_meta
            .exit_values
            .iter()
            .map(|(carrier_name, join_exit_value)| {
                // Get host_slot from variable_map
                let host_slot = builder
                    .variable_ctx
                    .variable_map
                    .get(carrier_name)
                    .copied()
                    .unwrap_or_else(|| {
                        panic!(
                            "[Phase 134 P0] Carrier '{}' not in variable_map (available: {:?})",
                            carrier_name,
                            builder.variable_ctx.variable_map.keys().collect::<Vec<_>>()
                        )
                    });

                LoopExitBinding {
                    carrier_name: carrier_name.clone(),
                    join_exit_value: *join_exit_value,
                    host_slot,
                    role: CarrierRole::LoopState,
                }
            })
            .collect();

        // Create boundary with DirectValue mode
        let mut boundary = JoinInlineBoundary::new_with_exit_bindings(
            vec![], // No join_inputs for Normalized
            vec![], // No host_inputs for Normalized
            exit_bindings,
        );
        boundary.exit_reconnect_mode = ExitReconnectMode::DirectValue; // No PHI
        boundary.continuation_func_ids = join_meta.continuation_funcs.clone();

        if debug {
            trace.routing(
                "normalization/execute/merge",
                func_name,
                &format!(
                    "Merging JoinModule: {} functions, {} exit bindings",
                    join_module.functions.len(),
                    boundary.exit_bindings.len()
                ),
            );
        }

        // Bridge JoinIR to MIR
        let empty_meta: JoinFuncMetaMap = BTreeMap::new();
        let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &empty_meta)
            .map_err(|e| format!("[normalization/execute] MIR conversion failed: {:?}", e))?;

        // Merge with boundary
        let _exit_phi_result = merge::merge_joinir_mir_blocks(
            builder,
            &mir_module,
            Some(&boundary),
            debug,
        )?;

        if debug {
            trace.routing(
                "normalization/execute/merge",
                func_name,
                "Merge + reconnection completed",
            );
        }

        Ok(())
    }
}
