/*!
 * MIR Builder - Converts AST to MIR/SSA form
 *
 * Implements AST → MIR conversion with SSA construction
 */

use super::slot_registry::resolve_slot_by_type_name;
use super::{
    BasicBlock, BasicBlockId, BasicBlockIdGenerator, CompareOp, ConstValue, Effect, EffectMask,
    FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, ValueIdGenerator,
};
use crate::ast::{ASTNode, LiteralValue, Span};
use crate::mir::builder::builder_calls::CallTarget;
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use crate::mir::region::RegionId;
use std::collections::HashSet;
use std::collections::{BTreeMap, HashMap};
mod binding_context; // Phase 136 follow-up (Step 4/7): BindingContext extraction
mod builder_calls;
mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities
mod calls; // Call system modules (refactored from builder_calls)
mod compilation_context; // Phase 136 follow-up (Step 7/7): CompilationContext extraction
mod context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離
mod core_context; // Phase 136 follow-up (Step 2/7): CoreContext extraction
mod decls; // declarations lowering split
mod exprs; // expression lowering split
mod exprs_call;
mod metadata_context; // Phase 136 follow-up (Step 6/7): MetadataContext extraction
mod method_call_handlers;
mod variable_context; // Phase 136 follow-up (Step 5/7): VariableContext extraction // Method call handler separation (Phase 3) // call(expr)
                      // include lowering removed (using is handled in runner)
mod control_flow; // thin wrappers to centralize control-flow entrypoints

// Phase 140-P4-A: Re-export for loop_canonicalizer SSOT (crate-wide visibility)
pub(crate) use control_flow::{detect_skip_whitespace_pattern, SkipWhitespaceInfo};
// Phase 104: Re-export read_digits(loop(true)) detection for loop_canonicalizer
pub(crate) use control_flow::{detect_read_digits_loop_true_pattern, ReadDigitsLoopTrueInfo};
// Phase 142-P1: Re-export continue pattern detection for loop_canonicalizer
pub(crate) use control_flow::{detect_continue_pattern, ContinuePatternInfo};
// Phase 143-P0: Re-export parse_number pattern detection for loop_canonicalizer
pub(crate) use control_flow::{detect_parse_number_pattern, ParseNumberInfo};
pub(crate) use control_flow::{detect_parse_string_pattern, ParseStringInfo};
// Phase 91 P5b: Re-export escape skip pattern detection for loop_canonicalizer
pub(crate) use control_flow::{detect_escape_skip_pattern, EscapeSkipPatternInfo};
mod exprs_lambda; // lambda lowering
mod exprs_peek; // peek expression
mod exprs_qmark; // ?-propagate
mod fields; // field access/assignment lowering split
mod if_form;
mod joinir_id_remapper; // Phase 189: JoinIR ID remapping (ValueId/BlockId translation)
mod joinir_inline_boundary_injector; // Phase 189: JoinInlineBoundary Copy instruction injector
mod lifecycle;
mod loop_frontend_binding; // Phase 50: Loop Frontend Binding (JoinIR variable mapping)
pub(crate) mod loops;
mod ops;
mod phi;
mod phi_merge; // Phase 25.1q: Unified PHI merge helper // prepare/lower_root/finalize split
               // legacy large-match remains inline for now (planned extraction)
mod emission; // emission::*（Const/Compare/Branch の薄い発行箱）
mod emit_guard; // EmitGuardBox（emit直前の最終関所）
mod metadata; // MetadataPropagationBox（type/originの伝播）
mod name_const; // NameConstBox（関数名Const生成）
mod observe; // P0: dev-only observability helpers（ssa/resolve）
mod origin; // P0: origin inference（me/Known）と PHI 伝播（軽量）
mod plugin_sigs; // plugin signature loader
mod receiver; // ReceiverMaterializationBox（Method recv の pin+LocalSSA 集約）
mod rewrite; // P1: Known rewrite & special consolidation
mod router; // RouterPolicyBox（Unified vs BoxCall）
mod schedule; // BlockScheduleBox（物理順序: PHI→materialize→body）
mod scope_context; // Phase 136 follow-up (Step 3/7): ScopeContext extraction
mod ssa; // LocalSSA helpers (in-block materialization)
mod stmts;
mod type_context; // Phase 136 follow-up: TypeContext extraction
mod type_facts; // Phase 136 follow-up: Type inference facts box
pub(crate) mod type_registry;
mod types; // types::annotation / inference（型注釈/推論の箱: 推論は後段）
mod utils;
mod vars; // variables/scope helpers // small loop helpers (header/exit context) // TypeRegistryBox（型情報管理の一元化）

// Unified member property kinds for computed/once/birth_once
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum PropertyKind {
    Computed,
    Once,
    BirthOnce,
}

/// MIR builder for converting AST to SSA form
pub struct MirBuilder {
    /// Current module being built
    pub(super) current_module: Option<MirModule>,

    /// Current basic block being built
    pub(super) current_block: Option<BasicBlockId>,

    /// Phase 136 follow-up (Step 2/7): Core ID generation context
    /// Consolidates value_gen, block_gen, next_binding_id, temp_slot_counter, debug_join_counter.
    /// Direct field access for backward compatibility (migration in progress).
    pub(super) core_ctx: core_context::CoreContext,

    /// Phase 136 follow-up: Type information context
    /// Consolidates value_types, value_kinds, value_origin_newbox for better organization.
    /// Direct field access for backward compatibility (migration in progress).
    pub(super) type_ctx: type_context::TypeContext,

    /// Phase 136 follow-up (Step 3/7): Scope and control flow context
    /// Consolidates lexical_scope_stack, loop stacks, if_merge_stack, current_function,
    /// function_param_names, debug_scope_stack for better organization.
    /// Direct field access for backward compatibility (migration in progress).
    pub(super) scope_ctx: scope_context::ScopeContext,

    /// Phase 136 follow-up (Step 4/7): Binding context
    /// Consolidates binding_map (String -> BindingId mapping).
    /// Direct field access for backward compatibility (migration in progress).
    pub(super) binding_ctx: binding_context::BindingContext,

    /// Phase 136 follow-up (Step 5/7): Variable context
    /// Consolidates variable_map (String -> ValueId mapping for SSA conversion).
    /// Direct field access for backward compatibility (migration in progress).
    pub(super) variable_ctx: variable_context::VariableContext,

    /// Phase 136 follow-up (Step 6/7): Metadata context
    /// Consolidates current_span, source_file, hint_sink, current_region_stack.
    /// Direct field access for backward compatibility (migration in progress).
    pub(super) metadata_ctx: metadata_context::MetadataContext,

    /// Phase 136 follow-up (Step 7/7): Compilation context
    /// Consolidates compilation_context, current_static_box, user_defined_boxes, reserved_value_ids,
    /// fn_body_ast, weak_fields_by_box, property_getters_by_box, field_origin_class, field_origin_by_box,
    /// static_method_index, method_tail_index, type_registry, current_slot_registry, plugin_method_sigs.
    /// Direct field access for backward compatibility (migration in progress).
    pub(super) comp_ctx: compilation_context::CompilationContext,

    /// Pending phi functions to be inserted
    #[allow(dead_code)]
    pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,

    // Phase 2-5: binding_map removed - use binding_ctx.binding_map instead

    // include guards removed
    // フェーズM: no_phi_modeフィールド削除（常にPHI使用）

    // ---- Try/Catch/Cleanup lowering context ----
    /// When true, `return` statements are deferred: they assign to `return_defer_slot`
    /// and jump to `return_defer_target` (typically the cleanup/exit block).
    pub(super) return_defer_active: bool,
    /// Slot value to receive deferred return values (edge-copy mode friendly).
    pub(super) return_defer_slot: Option<ValueId>,
    /// Target block to jump to on deferred return.
    pub(super) return_defer_target: Option<BasicBlockId>,
    /// Set to true when a deferred return has been emitted in the current context.
    pub(super) return_deferred_emitted: bool,
    /// True while lowering the cleanup block.
    pub(super) in_cleanup_block: bool,
    /// Policy flags (snapshotted at entry of try/catch lowering)
    pub(super) cleanup_allow_return: bool,
    pub(super) cleanup_allow_throw: bool,

    /// If true, skip entry materialization of pinned slots on the next start_new_block call.
    suppress_pin_entry_copy_next: bool,

    // ----------------------
    // Debug scope context (dev only; zero-cost when unused)
    // ----------------------
    /// Local SSA cache: ensure per-block materialization for critical operands (e.g., recv)
    /// Key: (bb, original ValueId, kind) -> local ValueId
    /// kind: 0=recv, 1+ reserved for future (args etc.)
    pub(super) local_ssa_map: HashMap<(BasicBlockId, ValueId, u8), ValueId>,
    /// BlockSchedule cache: deduplicate materialize copies per (bb, src)
    pub(super) schedule_mat_map: HashMap<(BasicBlockId, ValueId), ValueId>,
    /// Mapping from ValueId to its pin slot name (e.g., "__pin$3$@recv")
    /// Used by LocalSSA to redirect old pinned values to the latest slot value.
    pub(super) pin_slot_names: HashMap<ValueId, String>,

    /// Guard flag to prevent re-entering emit_unified_call from BoxCall fallback.
    /// Used when RouterPolicyBox in emit_unified_call has already decided to
    /// route a given Method call to BoxCall; emit_box_or_plugin_call must not
    /// bounce back into the unified path for the same call, otherwise an
    /// infinite recursion (emit_unified_call → emit_box_or_plugin_call →
    /// emit_unified_call …) can occur when routing decisions disagree.
    pub(super) in_unified_boxcall_fallback: bool,

    /// Recursion depth counter for debugging stack overflow
    /// Tracks the depth of build_expression calls to detect infinite loops
    pub(super) recursion_depth: usize,

    /// Root lowering mode: how to treat top-level Program
    /// - None: not decided yet (lower_root not called)
    /// - Some(true): App mode (static box Main.main is entry)
    /// - Some(false): Script/Test mode (top-level Program runs sequentially)
    pub(super) root_is_app_mode: Option<bool>,

    /// 🎯 Phase 21.7: Static box singleton instances for methodization
    /// Maps BoxName → ValueId of singleton instance (created on demand)
    /// Used when HAKO_MIR_BUILDER_METHODIZE=1 to convert Global("BoxName.method/arity")
    /// to Method{receiver=singleton} calls
    pub(super) static_box_singletons: HashMap<String, ValueId>,
}

impl MirBuilder {
    /// Create a new MIR builder
    pub fn new() -> Self {
        let plugin_method_sigs = plugin_sigs::load_plugin_method_sigs();
        let core_ctx = core_context::CoreContext::new();

        // Phase 136 Step 7/7: Compilation context (new SSOT)
        let comp_ctx =
            compilation_context::CompilationContext::with_plugin_sigs(plugin_method_sigs.clone());

        // フェーズM: no_phi_mode初期化削除
        #[allow(deprecated)]
        Self {
            current_module: None,
            current_block: None,

            // Phase 136 Step 2/7: Core context (new SSOT)
            core_ctx,

            type_ctx: type_context::TypeContext::new(), // Phase 136: Type context
            scope_ctx: scope_context::ScopeContext::new(), // Phase 136 Step 3/7: Scope context
            binding_ctx: binding_context::BindingContext::new(), // Phase 136 Step 4/7: Binding context
            variable_ctx: variable_context::VariableContext::new(), // Phase 136 Step 5/7: Variable context
            metadata_ctx: metadata_context::MetadataContext::new(), // Phase 136 Step 6/7: Metadata context
            comp_ctx, // Phase 136 Step 7/7: Compilation context
            pending_phis: Vec::new(),

            // Phase 2-5: binding_map initialization removed

            // フェーズM: no_phi_modeフィールド削除
            return_defer_active: false,
            return_defer_slot: None,
            return_defer_target: None,
            return_deferred_emitted: false,
            in_cleanup_block: false,
            cleanup_allow_return: false,
            cleanup_allow_throw: false,
            suppress_pin_entry_copy_next: false,

            local_ssa_map: HashMap::new(),
            schedule_mat_map: HashMap::new(),
            pin_slot_names: HashMap::new(),

            in_unified_boxcall_fallback: false,
            recursion_depth: 0,
            root_is_app_mode: None,
            static_box_singletons: HashMap::new(), // Phase 21.7: methodization support
        }
    }

    // Phase 2-5: BindingContext sync helpers removed - binding_ctx is now SSOT
    // Phase 2-6: VariableContext sync helpers removed - variable_ctx is now SSOT

    /// Push/pop helpers for If merge context (best-effort; optional usage)
    pub(super) fn push_if_merge(&mut self, bb: BasicBlockId) {
        // Phase 2-4: Use scope_ctx only (legacy field removed)
        self.scope_ctx.push_if_merge(bb);
    }
    pub(super) fn pop_if_merge(&mut self) {
        // Phase 2-4: Use scope_ctx only (legacy field removed)
        let _ = self.scope_ctx.pop_if_merge();
    }

    /// Suppress entry pin copy for the next start_new_block (used for merge blocks).
    pub(super) fn suppress_next_entry_pin_copy(&mut self) {
        self.suppress_pin_entry_copy_next = true;
    }

    // ---- Phase 74: BindingId allocation ----
    /// Allocate a new BindingId (parallel to ValueId allocation)
    ///
    /// ## Parallel ValueId/BindingId Allocation
    ///
    /// BindingId allocation is completely independent from ValueId allocation:
    /// - `next_value_id()` increments `value_gen` counter
    /// - `allocate_binding_id()` increments `next_binding_id` counter
    ///
    /// This parallelism enables:
    /// 1. **Stable binding identity** across SSA transformations
    /// 2. **Independent shadowing tracking** separate from SSA renaming
    /// 3. **Future ScopeManager migration** (Phase 75+) without breaking SSA
    ///
    /// Example:
    /// ```ignore
    /// // local x = 1;      <- allocate_binding_id() -> BindingId(0)
    /// //                      next_value_id() -> ValueId(10)
    /// // {
    /// //   local x = 2;    <- allocate_binding_id() -> BindingId(1)
    /// //                      next_value_id() -> ValueId(20)
    /// // }
    /// ```
    pub fn allocate_binding_id(&mut self) -> super::BindingId {
        // Phase 136 Step 2/7 + Phase 2-2: Use core_ctx as SSOT (no sync needed)
        self.core_ctx.next_binding()
    }

    // ---- Hint helpers (no-op by default) ----
    // Phase 136 Step 6/7: Delegate to metadata_ctx with legacy sync
    #[inline]
    pub(crate) fn hint_scope_enter(&mut self, id: u32) {
        self.metadata_ctx.hint_scope_enter(id);
    }
    #[inline]
    pub(crate) fn hint_scope_leave(&mut self, id: u32) {
        self.metadata_ctx.hint_scope_leave(id);
    }
    #[inline]
    pub(crate) fn hint_join_result<S: Into<String>>(&mut self, var: S) {
        self.metadata_ctx.hint_join_result(var);
    }

    // ----------------------
    // Debug scope helpers (region_id for DebugHub events)
    // ----------------------
    #[inline]
    pub(crate) fn debug_next_join_id(&mut self) -> u32 {
        // Phase 136 Step 2/7 + Phase 2-2: Use core_ctx as SSOT (no sync needed)
        self.core_ctx.next_debug_join()
    }

    #[inline]
    pub(crate) fn debug_push_region<S: Into<String>>(&mut self, region: S) {
        // Phase 2-4: Use scope_ctx only (legacy field removed)
        let region = region.into();
        self.scope_ctx.debug_push_region(region);
    }

    #[inline]
    pub(crate) fn debug_pop_region(&mut self) {
        // Phase 2-4: Use scope_ctx only (legacy field removed)
        self.scope_ctx.debug_pop_region();
    }

    #[inline]
    #[allow(deprecated)]
    pub(crate) fn debug_current_region_id(&self) -> Option<String> {
        // Phase 136 Step 3/7: Read from scope_ctx (SSOT)
        self.scope_ctx.debug_current_region_id()
    }

    /// Hint for downstream metadata: set the logical source file name/path for the next build.
    /// Phase 136 Step 6/7: Delegate to metadata_ctx
    pub fn set_source_file_hint<S: Into<String>>(&mut self, source: S) {
        self.metadata_ctx.set_source_file(source);
    }

    /// Clear the source file hint (used when reusing the builder across modules).
    /// Phase 136 Step 6/7: Delegate to metadata_ctx
    pub fn clear_source_file_hint(&mut self) {
        self.metadata_ctx.clear_source_file();
    }

    /// Resolve current source file hint (builder field or env fallback).
    /// Phase 136 Step 6/7: Delegate to metadata_ctx
    fn current_source_file(&self) -> Option<String> {
        self.metadata_ctx
            .current_source_file()
            .or_else(|| std::env::var("NYASH_SOURCE_FILE_HINT").ok())
    }

    /// Create a new MirFunction with source metadata applied.
    fn new_function_with_metadata(
        &self,
        signature: FunctionSignature,
        entry_block: BasicBlockId,
    ) -> MirFunction {
        let mut f = MirFunction::new(signature, entry_block);
        f.metadata.source_file = self.current_source_file();
        f
    }

    // ----------------------
    // Compile trace helpers (dev only; env-gated)
    // ----------------------
    #[inline]
    pub(super) fn compile_trace_enabled() -> bool {
        std::env::var("NYASH_MIR_COMPILE_TRACE").ok().as_deref() == Some("1")
    }

    #[inline]
    pub(super) fn trace_compile<S: AsRef<str>>(&self, msg: S) {
        if Self::compile_trace_enabled() {
            eprintln!("[mir-compile] {}", msg.as_ref());
        }
    }

    // ----------------------
    // Method tail index (performance helper)
    // ----------------------
    fn rebuild_method_tail_index(&mut self) {
        self.comp_ctx.method_tail_index.clear();
        if let Some(ref module) = self.current_module {
            for name in module.functions.keys() {
                if let (Some(dot), Some(slash)) = (name.rfind('.'), name.rfind('/')) {
                    if slash > dot {
                        let tail = &name[dot..];
                        self.comp_ctx
                            .method_tail_index
                            .entry(tail.to_string())
                            .or_insert_with(Vec::new)
                            .push(name.clone());
                    }
                }
            }
            self.comp_ctx.method_tail_index_source_len = module.functions.len();
        } else {
            self.comp_ctx.method_tail_index_source_len = 0;
        }
    }

    fn ensure_method_tail_index(&mut self) {
        let need_rebuild = match self.current_module {
            Some(ref refmod) => {
                self.comp_ctx.method_tail_index_source_len != refmod.functions.len()
            }
            None => self.comp_ctx.method_tail_index_source_len != 0,
        };
        if need_rebuild {
            self.rebuild_method_tail_index();
        }
    }

    pub(super) fn method_candidates(&mut self, method: &str, arity: usize) -> Vec<String> {
        self.ensure_method_tail_index();
        let tail = format!(".{}{}", method, format!("/{}", arity));
        self.comp_ctx
            .method_tail_index
            .get(&tail)
            .cloned()
            .unwrap_or_default()
    }

    pub(super) fn method_candidates_tail<S: AsRef<str>>(&mut self, tail: S) -> Vec<String> {
        self.ensure_method_tail_index();
        self.comp_ctx
            .method_tail_index
            .get(tail.as_ref())
            .cloned()
            .unwrap_or_default()
    }

    /// Build a complete MIR module from AST
    pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
        self.prepare_module()?;
        let result_value = self.lower_root(ast)?;
        self.finalize_module(result_value)
    }

    /// Build an expression and return its value ID
    pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result<ValueId, String> {
        // Delegated to exprs.rs to keep this file lean
        // Debug: Track recursion depth to detect infinite loops
        const MAX_RECURSION_DEPTH: usize = 200;
        self.recursion_depth += 1;
        if self.recursion_depth > MAX_RECURSION_DEPTH {
            eprintln!("\n[FATAL] ============================================");
            eprintln!(
                "[FATAL] Recursion depth exceeded {} in build_expression",
                MAX_RECURSION_DEPTH
            );
            eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
            eprintln!("[FATAL] AST node type: {:?}", std::mem::discriminant(&ast));
            eprintln!("[FATAL] ============================================\n");
            return Err(format!(
                "Recursion depth exceeded: {} (possible infinite loop)",
                self.recursion_depth
            ));
        }

        let result = self.build_expression_impl(ast);
        self.recursion_depth -= 1;
        result
    }

    /// Build a literal value
    pub(super) fn build_literal(&mut self, literal: LiteralValue) -> Result<ValueId, String> {
        // Determine type without moving literal
        let ty_for_dst = match &literal {
            LiteralValue::Integer(_) => Some(super::MirType::Integer),
            LiteralValue::Float(_) => Some(super::MirType::Float),
            LiteralValue::Bool(_) => Some(super::MirType::Bool),
            LiteralValue::String(_) => Some(super::MirType::String),
            _ => None,
        };

        // Emit via ConstantEmissionBox（仕様不変の統一ルート）
        let dst = match literal {
            LiteralValue::Integer(n) => {
                crate::mir::builder::emission::constant::emit_integer(self, n)
            }
            LiteralValue::Float(f) => crate::mir::builder::emission::constant::emit_float(self, f),
            LiteralValue::String(s) => {
                crate::mir::builder::emission::constant::emit_string(self, s)
            }
            LiteralValue::Bool(b) => crate::mir::builder::emission::constant::emit_bool(self, b),
            LiteralValue::Null => crate::mir::builder::emission::constant::emit_null(self),
            LiteralValue::Void => crate::mir::builder::emission::constant::emit_void(self),
        };
        // Annotate type
        if let Some(ty) = ty_for_dst {
            self.type_ctx.value_types.insert(dst, ty);
        }

        Ok(dst)
    }

    /// Build variable access
    pub(super) fn build_variable_access(&mut self, name: String) -> Result<ValueId, String> {
        // Step 5-5-G: __pin$ variables should NEVER be accessed from variable_map
        // They are transient temporaries created during expression building and
        // should not persist across blocks. If we see one here, it's a compiler bug.
        if name.starts_with("__pin$") {
            return Err(format!(
                "COMPILER BUG: Attempt to access __pin$ temporary '{}' from variable_map. \
                 __pin$ variables should only exist as direct SSA values, not as named variables.",
                name
            ));
        }

        if let Some(&value_id) = self.variable_ctx.variable_map.get(&name) {
            Ok(value_id)
        } else {
            Err(self.undefined_variable_message(&name))
        }
    }

    pub(in crate::mir::builder) fn undefined_variable_message(&self, name: &str) -> String {
        // Enhance diagnostics using Using simple registry (Phase 1)
        let mut msg = format!("Undefined variable: {}", name);

        // Stage-3 keyword diagnostic (local/flow/try/catch/throw)
        if name == "local" && !crate::config::env::parser_stage3_enabled() {
            msg.push_str("\nHint: 'local' is a Stage-3 keyword. Prefer NYASH_FEATURES=stage3 (legacy: NYASH_PARSER_STAGE3=1 / HAKO_PARSER_STAGE3=1 for Stage-B).");
            msg.push_str("\nFor AotPrep verification, use tools/hakorune_emit_mir.sh which sets these automatically.");
        } else if (name == "flow" || name == "try" || name == "catch" || name == "throw")
            && !crate::config::env::parser_stage3_enabled()
        {
            msg.push_str(&format!("\nHint: '{}' is a Stage-3 keyword. Prefer NYASH_FEATURES=stage3 (legacy: NYASH_PARSER_STAGE3=1 / HAKO_PARSER_STAGE3=1 for Stage-B).", name));
        }

        let suggest = crate::using::simple_registry::suggest_using_for_symbol(name);
        if !suggest.is_empty() {
            msg.push_str("\nHint: symbol appears in using module(s): ");
            msg.push_str(&suggest.join(", "));
            msg.push_str(
                "\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using].",
            );
        }

        msg
    }

    /// Build assignment
    pub(super) fn build_assignment(
        &mut self,
        var_name: String,
        value: ASTNode,
    ) -> Result<ValueId, String> {
        // SSOT (LANGUAGE_REFERENCE_2025 / syntax-cheatsheet):
        // - Assignment to an undeclared name is an error.
        // - Use `local name = ...` (or `local name; name = ...`) to declare.
        vars::assignment_resolver::AssignmentResolverBox::ensure_declared(self, &var_name)?;

        let value_id = self.build_expression(value)?;

        // Step 5-5-E: FIX variable map corruption bug
        // REMOVED pin_to_slot() call - it was causing __pin$ temporaries to overwrite
        // real variable names in the variable map.
        //
        // Root cause: pin_to_slot(raw_value_id, "@assign") would sometimes return
        // a ValueId from a previous __pin$ temporary (e.g., __pin$767$@binop_lhs),
        // causing variable_map["m"] to point to the wrong ValueId.
        //
        // SSA + PHI merges work correctly without explicit pinning here.
        // The expression building already creates necessary temporaries.

        // Step 5-5-F: NEVER insert __pin$ temporaries into variable_map
        // __pin$ variables are transient compiler-generated temporaries that should
        // never be tracked as real variables. They are used only within expression
        // building and should not persist across blocks or loops.
        //
        // BUG FIX: Previously, __pin$ variables would be inserted into variable_map,
        // causing stale references after LoopForm transformation renumbers blocks.
        // Result: VM would try to read undefined ValueIds (e.g., ValueId(270) at bb303).
        if !var_name.starts_with("__pin$") {
            // In SSA form, each assignment creates a new value
            self.variable_ctx
                .variable_map
                .insert(var_name.clone(), value_id);
        }

        Ok(value_id)
    }

    /// Emit an instruction to the current basic block
    pub(super) fn emit_instruction(&mut self, instruction: MirInstruction) -> Result<(), String> {
        let block_id = self.current_block.ok_or("No current basic block")?;

        // Make instruction mutable for potential receiver materialization
        let mut instruction = instruction;

        // Precompute debug metadata to avoid borrow conflicts later
        let _dbg_fn_name = self
            .scope_ctx
            .current_function
            .as_ref()
            .map(|f| f.signature.name.clone());
        let _dbg_region_id = self.debug_current_region_id();
        // P0: PHI の軽量補強と観測は、関数ブロック取得前に実施して借用競合を避ける
        if let MirInstruction::Phi { dst, inputs, .. } = &instruction {
            origin::phi::propagate_phi_meta(self, *dst, inputs);
            observe::ssa::emit_phi(self, *dst, inputs);
        }

        // CRITICAL: Final receiver materialization for MethodCall
        // This ensures the receiver has an in-block definition in the same block as the Call.
        // Must happen BEFORE function mutable borrow to avoid borrowck conflicts.
        if let MirInstruction::Call {
            callee: Some(callee),
            dst,
            args,
            effects,
            ..
        } = &instruction
        {
            use crate::mir::definitions::call_unified::Callee;
            if let Callee::Method {
                box_name,
                method,
                receiver: Some(r),
                certainty,
                box_kind,
            } = callee.clone()
            {
                // LocalSSA: ensure receiver has a Copy in current_block
                let r_local = crate::mir::builder::ssa::local::recv(self, r);

                // Update instruction with materialized receiver
                let new_callee = Callee::Method {
                    box_name: box_name.clone(),
                    method: method.clone(),
                    receiver: Some(r_local),
                    certainty,
                    box_kind,
                };
                instruction = MirInstruction::Call {
                    dst: *dst,
                    func: crate::mir::ValueId::INVALID, // Legacy dummy (not a real SSA use)
                    callee: Some(new_callee),
                    args: args.clone(),
                    effects: *effects,
                };
            }
        }

        if let Some(ref mut function) = self.scope_ctx.current_function {
            // Pre-capture branch/jump targets for predecessor update after we finish
            // mutably borrowing the current block.
            let (then_t, else_t, jump_t) = match &instruction {
                MirInstruction::Branch {
                    then_bb, else_bb, ..
                } => (Some(*then_bb), Some(*else_bb), None),
                MirInstruction::Jump { target } => (None, None, Some(*target)),
                _ => (None, None, None),
            };

            // Extract function name before mutable borrow to avoid borrowck error
            let current_fn_name = function.signature.name.clone();

            if let Some(block) = function.get_block_mut(block_id) {
                // CRITICAL: Copy専用トレース（LocalSSA調査用）
                if let MirInstruction::Copy { dst, src } = &instruction {
                    if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
                        eprintln!(
                            "[emit-inst] fn={} bb={:?} COPY %{} <- %{}",
                            current_fn_name,
                            self.current_block.map(|b| b.0).unwrap_or(0),
                            dst.0,
                            src.0
                        );
                    }
                }

                // Invariant: Call must always carry a Callee (unified path).
                if let MirInstruction::Call { callee, .. } = &instruction {
                    if callee.is_none() {
                        return Err("builder invariant violated: MirInstruction::Call.callee must be Some (unified call)".into());
                    } else if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
                        use crate::mir::definitions::call_unified::Callee;
                        if let Some(Callee::Method {
                            box_name,
                            method,
                            receiver: Some(r),
                            ..
                        }) = callee
                        {
                            eprintln!(
                                "[emit-inst] fn={} bb={:?} Call {}.{} recv=%{}",
                                current_fn_name,
                                self.current_block.map(|b| b.0).unwrap_or(0),
                                box_name,
                                method,
                                r.0
                            );
                        }
                    } else if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1")
                    {
                        use crate::mir::definitions::call_unified::Callee;
                        if let Some(Callee::Method {
                            box_name,
                            method,
                            receiver: Some(r),
                            ..
                        }) = callee
                        {
                            let names: Vec<String> = self
                                .variable_ctx
                                .variable_map
                                .iter()
                                .filter(|(_, &vid)| vid == *r)
                                .map(|(k, _)| k.clone())
                                .collect();
                            eprintln!(
                                "[builder/recv-trace] fn={} bb={:?} method={}.{} recv=%{} aliases={:?}",
                                current_fn_name,
                                self.current_block,
                                box_name,
                                method,
                                r.0,
                                names
                            );
                        }
                    }
                }
                if utils::builder_debug_enabled() {
                    eprintln!(
                        "[BUILDER] emit @bb{} -> {}",
                        block_id,
                        match &instruction {
                            MirInstruction::TypeOp { dst, op, value, ty } =>
                                format!("typeop {:?} {} {:?} -> {}", op, value, ty, dst),
                            MirInstruction::Print { value, .. } => format!("print {}", value),
                            MirInstruction::BoxCall {
                                box_val,
                                method,
                                method_id,
                                args,
                                dst,
                                ..
                            } => {
                                if let Some(mid) = method_id {
                                    format!(
                                        "boxcall {}.{}[#{}]({:?}) -> {:?}",
                                        box_val, method, mid, args, dst
                                    )
                                } else {
                                    format!(
                                        "boxcall {}.{}({:?}) -> {:?}",
                                        box_val, method, args, dst
                                    )
                                }
                            }
                            MirInstruction::Call {
                                func, args, dst, ..
                            } => format!("call {}({:?}) -> {:?}", func, args, dst),
                            MirInstruction::NewBox {
                                dst,
                                box_type,
                                args,
                            } => format!("new {}({:?}) -> {}", box_type, args, dst),
                            MirInstruction::Const { dst, value } =>
                                format!("const {:?} -> {}", value, dst),
                            MirInstruction::Branch {
                                condition,
                                then_bb,
                                else_bb,
                            } => format!("br {}, {}, {}", condition, then_bb, else_bb),
                            MirInstruction::Jump { target } => format!("br {}", target),
                            _ => format!("{:?}", instruction),
                        }
                    );
                }
                // Phase 136 Step 6/7: Use metadata_ctx for span
                block.add_instruction_with_span(
                    instruction.clone(),
                    self.metadata_ctx.current_span(),
                );
                // Drop the mutable borrow of `block` before updating other blocks
            }
            // Update predecessor sets for branch/jump immediately so that
            // debug_verify_phi_inputs can observe a consistent CFG without
            // requiring a full function.update_cfg() pass.
            if let Some(t) = then_t {
                if let Some(succ) = function.get_block_mut(t) {
                    succ.add_predecessor(block_id);
                }
            }
            if let Some(t) = else_t {
                if let Some(succ) = function.get_block_mut(t) {
                    succ.add_predecessor(block_id);
                }
            }
            if let Some(t) = jump_t {
                if let Some(succ) = function.get_block_mut(t) {
                    succ.add_predecessor(block_id);
                }
            }
            Ok(())
        } else {
            Err(format!("Basic block {} does not exist", block_id))
        }
    }

    /// Update an existing PHI instruction's inputs (for loop sealing)
    /// Used by LoopFormBuilder to complete incomplete PHI nodes
    #[allow(dead_code)]
    pub(super) fn update_phi_instruction(
        &mut self,
        block: BasicBlockId,
        phi_id: ValueId,
        new_inputs: Vec<(BasicBlockId, ValueId)>,
    ) -> Result<(), String> {
        if let Some(ref mut function) = self.scope_ctx.current_function {
            if let Some(block_data) = function.get_block_mut(block) {
                // Find PHI instruction with matching dst
                for inst in &mut block_data.instructions {
                    if let MirInstruction::Phi { dst, inputs, .. } = inst {
                        if *dst == phi_id {
                            *inputs = new_inputs;
                            return Ok(());
                        }
                    }
                }
                Err(format!(
                    "PHI instruction {} not found in block {}",
                    phi_id, block
                ))
            } else {
                Err(format!("Block {} not found", block))
            }
        } else {
            Err("No current function".to_string())
        }
    }

    // フェーズM: is_no_phi_mode()メソッド削除

    // フェーズM: insert_edge_copy()メソッド削除（no_phi_mode撤廃により不要）

    /// Build new expression: new ClassName(arguments)
    pub(super) fn build_new_expression(
        &mut self,
        class: String,
        arguments: Vec<ASTNode>,
    ) -> Result<ValueId, String> {
        // Phase 9.78a: Unified Box creation using NewBox instruction
        // Core-13 pure mode: emit ExternCall(env.box.new) with type name const only
        if crate::config::env::mir_core13_pure() {
            // Emit Const String for type name（ConstantEmissionBox）
            let ty_id = crate::mir::builder::emission::constant::emit_string(self, class.clone());
            // Evaluate arguments (pass through to env.box.new shim)
            let mut arg_vals: Vec<ValueId> = Vec::with_capacity(arguments.len());
            for a in arguments {
                arg_vals.push(self.build_expression(a)?);
            }
            // Build arg list: [type, a1, a2, ...]
            let mut args: Vec<ValueId> = Vec::with_capacity(1 + arg_vals.len());
            args.push(ty_id);
            args.extend(arg_vals);
            // Call env.box.new
            // 📦 Hotfix 3: Use next_value_id() to respect function parameter reservation
            let dst = self.next_value_id();
            self.emit_instruction(MirInstruction::ExternCall {
                dst: Some(dst),
                iface_name: "env.box".to_string(),
                method_name: "new".to_string(),
                args,
                effects: EffectMask::PURE,
            })?;
            // 型注釈（最小）
            self.type_ctx
                .value_types
                .insert(dst, super::MirType::Box(class.clone()));
            return Ok(dst);
        }

        // Optimization: Primitive wrappers → emit Const directly when possible
        if class == "IntegerBox" && arguments.len() == 1 {
            if let ASTNode::Literal {
                value: LiteralValue::Integer(n),
                ..
            } = arguments[0].clone()
            {
                // 📦 Hotfix 3: Use next_value_id() to respect function parameter reservation
                let dst = self.next_value_id();
                self.emit_instruction(MirInstruction::Const {
                    dst,
                    value: ConstValue::Integer(n),
                })?;
                self.type_ctx
                    .value_types
                    .insert(dst, super::MirType::Integer);
                return Ok(dst);
            }
        }

        // First, evaluate all arguments to get their ValueIds
        let mut arg_values = Vec::new();
        for arg in arguments {
            let arg_value = self.build_expression(arg)?;
            arg_values.push(arg_value);
        }

        // Generate the destination ValueId
        // 📦 Hotfix 3: Use next_value_id() to respect function parameter reservation
        let dst = self.next_value_id();

        // Emit NewBox instruction for all Box types
        // VM will handle optimization for basic types internally
        self.emit_instruction(MirInstruction::NewBox {
            dst,
            box_type: class.clone(),
            args: arg_values.clone(),
        })?;
        // Phase 15.5: Unified box type handling
        // All boxes (including former core boxes) are treated uniformly as Box types
        self.type_ctx
            .value_types
            .insert(dst, super::MirType::Box(class.clone()));

        // Record origin for optimization: dst was created by NewBox of class
        self.type_ctx.value_origin_newbox.insert(dst, class.clone());

        // birth 呼び出し（Builder 正規化）
        // 優先: 低下済みグローバル関数 `<Class>.birth/Arity`（Arity は me を含まない）
        // 代替: 既存互換として BoxCall("birth")（プラグイン/ビルトインの初期化に対応）
        if class != "StringBox" {
            let arity = arg_values.len();
            let lowered =
                crate::mir::builder::calls::function_lowering::generate_method_function_name(
                    &class, "birth", arity,
                );
            let use_lowered = if let Some(ref module) = self.current_module {
                module.functions.contains_key(&lowered)
            } else {
                false
            };
            if use_lowered {
                // Call Global("Class.birth/Arity") with argv = [me, args...]
                let mut argv: Vec<ValueId> = Vec::with_capacity(1 + arity);
                argv.push(dst);
                argv.extend(arg_values.iter().copied());
                self.emit_legacy_call(None, CallTarget::Global(lowered), argv)?;
            } else {
                // Fallback policy:
                // - For user-defined boxes (no explicit constructor), do NOT emit BoxCall("birth").
                //   VM will treat plain NewBox as constructed; dev verify warns if needed.
                // - For builtins/plugins, keep BoxCall("birth") fallback to preserve legacy init.
                let is_user_box = self.comp_ctx.user_defined_boxes.contains(&class);
                // Dev safety: allow disabling birth() injection for builtins to avoid
                // unified-call method dispatch issues while migrating. Off by default unless explicitly enabled.
                let allow_builtin_birth = std::env::var("NYASH_DEV_BIRTH_INJECT_BUILTINS")
                    .ok()
                    .as_deref()
                    == Some("1");
                if !is_user_box && allow_builtin_birth {
                    let birt_mid = resolve_slot_by_type_name(&class, "birth");
                    self.emit_box_or_plugin_call(
                        None,
                        dst,
                        "birth".to_string(),
                        birt_mid,
                        arg_values,
                        EffectMask::READ.add(Effect::ReadHeap),
                    )?;
                }
            }
        }

        Ok(dst)
    }

    /// Check if the current basic block is terminated
    fn is_current_block_terminated(&self) -> bool {
        if let (Some(block_id), Some(ref function)) =
            (self.current_block, &self.scope_ctx.current_function)
        {
            if let Some(block) = function.get_block(block_id) {
                return block.is_terminated();
            }
        }
        false
    }

    // ============================================================================
    // Phase 26-A: ValueId型安全化メソッド
    // ============================================================================

    /// 型付きValueIdを発行（新API）
    /// Phase 136 P0: Use SSOT allocator (next_value_id) to respect function context
    pub fn new_typed_value(&mut self, kind: super::MirValueKind) -> super::TypedValueId {
        let id = self.next_value_id();
        self.type_ctx.value_kinds.insert(id, kind);
        super::TypedValueId::new(id, kind)
    }

    /// 既存ValueIdの型情報を取得
    pub fn get_value_kind(&self, id: ValueId) -> Option<super::MirValueKind> {
        self.type_ctx.value_kinds.get(&id).copied()
    }

    /// 既存ValueIdに型情報を後付け（レガシー互換用）
    pub fn register_value_kind(&mut self, id: ValueId, kind: super::MirValueKind) {
        self.type_ctx.value_kinds.insert(id, kind);
    }

    /// 型安全なパラメータ判定（ValueIdベース） - GUARD Bug Prevention
    pub fn is_value_parameter(&self, id: ValueId) -> bool {
        self.get_value_kind(id)
            .map(|kind| kind.is_parameter())
            .unwrap_or(false)
    }

    /// 型安全なローカル変数判定（ValueIdベース）
    pub fn is_value_local(&self, id: ValueId) -> bool {
        self.get_value_kind(id)
            .map(|kind| kind.is_local())
            .unwrap_or(false)
    }

    /// 型安全なLoopCarrier判定（ValueIdベース）
    pub fn is_value_loop_carrier(&self, id: ValueId) -> bool {
        self.get_value_kind(id)
            .map(|kind| kind.is_loop_carrier())
            .unwrap_or(false)
    }
}

impl Default for MirBuilder {
    fn default() -> Self {
        Self::new()
    }
}

// Phase 79: BindingMapProvider implementation
// Centralizes feature-gated binding_map access for promoters
use crate::mir::loop_pattern_detection::BindingMapProvider;

impl BindingMapProvider for MirBuilder {
    #[cfg(feature = "normalized_dev")]
    fn get_binding_map(
        &self,
    ) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
        // Phase 136 Step 4/7: Use binding_ctx (SSOT)
        Some(self.binding_ctx.binding_map())
    }

    #[cfg(not(feature = "normalized_dev"))]
    fn get_binding_map(
        &self,
    ) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
        None
    }
}

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

    #[test]
    fn test_binding_map_initialization() {
        let builder = MirBuilder::new();
        assert_eq!(builder.core_ctx.next_binding_id, 0);
        // Phase 2-6: binding_ctx is now SSOT (legacy field removed)
        assert!(builder.binding_ctx.is_empty());
    }

    #[test]
    fn test_binding_allocation_sequential() {
        let mut builder = MirBuilder::new();
        let bid0 = builder.allocate_binding_id();
        let bid1 = builder.allocate_binding_id();
        let bid2 = builder.allocate_binding_id();

        assert_eq!(bid0.raw(), 0);
        assert_eq!(bid1.raw(), 1);
        assert_eq!(bid2.raw(), 2);
        assert_eq!(builder.core_ctx.next_binding_id, 3);
    }

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

        // Simulate function entry scope
        builder.push_lexical_scope();

        // Declare outer x
        // Phase 136 P0: Use SSOT allocator for function scope simulation
        let outer_vid = builder.next_value_id();
        builder
            .declare_local_in_current_scope("x", outer_vid)
            .unwrap();
        // Phase 2-6: Check binding_ctx (SSOT)
        let outer_bid = builder.binding_ctx.lookup("x").unwrap();
        assert_eq!(outer_bid.raw(), 0);

        // Enter inner scope and shadow x
        builder.push_lexical_scope();
        // Phase 136 P0: Use SSOT allocator for function scope simulation
        let inner_vid = builder.next_value_id();
        builder
            .declare_local_in_current_scope("x", inner_vid)
            .unwrap();
        // Phase 2-6: Check binding_ctx (SSOT)
        let inner_bid = builder.binding_ctx.lookup("x").unwrap();
        assert_eq!(inner_bid.raw(), 1);

        // Exit inner scope - should restore outer binding
        builder.pop_lexical_scope();
        // Phase 2-6: Check binding_ctx (SSOT)
        let restored_bid = builder.binding_ctx.lookup("x").unwrap();
        assert_eq!(restored_bid, outer_bid);
        assert_eq!(restored_bid.raw(), 0);

        // Cleanup
        builder.pop_lexical_scope();
    }

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

        // Phase 136 P0: Use SSOT allocator (next_value_id)
        // Note: Without current_function, next_value_id() falls back to value_gen.next()
        // so this test still validates ValueId/BindingId independence
        // Allocate ValueIds and BindingIds in parallel
        let vid0 = builder.next_value_id();
        let bid0 = builder.allocate_binding_id();
        let vid1 = builder.next_value_id();
        let bid1 = builder.allocate_binding_id();

        // ValueId and BindingId should be independent
        assert_eq!(vid0.0, 0);
        assert_eq!(bid0.raw(), 0);
        assert_eq!(vid1.0, 1);
        assert_eq!(bid1.raw(), 1);

        // Allocating more ValueIds should not affect BindingId counter
        let _ = builder.next_value_id();
        let _ = builder.next_value_id();
        let bid2 = builder.allocate_binding_id();
        assert_eq!(bid2.raw(), 2); // Still sequential

        // Allocating more BindingIds should not affect ValueId counter
        let _ = builder.allocate_binding_id();
        let _ = builder.allocate_binding_id();
        let vid2 = builder.next_value_id();
        assert_eq!(vid2.0, 4); // Continues from where we left off
    }
}
