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

use super::{
    MirInstruction, BasicBlock, BasicBlockId, MirFunction, MirModule, 
    FunctionSignature, ValueId, ConstValue, BinaryOp, UnaryOp, CompareOp,
    MirType, EffectMask, Effect, BasicBlockIdGenerator, ValueIdGenerator
};
use super::slot_registry::{get_or_assign_type_id, reserve_method_slot};
use super::slot_registry::resolve_slot_by_type_name;
use crate::ast::{ASTNode, LiteralValue, BinaryOperator};
use std::collections::HashMap;
use std::collections::HashSet;
use std::fs;
mod builder_calls;
mod stmts;
mod ops;
mod utils;

// moved helpers to builder/utils.rs

/// MIR builder for converting AST to SSA form
pub struct MirBuilder {
    /// Current module being built
    pub(super) current_module: Option<MirModule>,
    
    /// Current function being built
    pub(super) current_function: Option<MirFunction>,
    
    /// Current basic block being built
    pub(super) current_block: Option<BasicBlockId>,
    
    /// Value ID generator
    pub(super) value_gen: ValueIdGenerator,
    
    /// Basic block ID generator
    pub(super) block_gen: BasicBlockIdGenerator,
    
    /// Variable name to ValueId mapping (for SSA conversion)
    pub(super) variable_map: HashMap<String, ValueId>,
    
    /// Pending phi functions to be inserted
    #[allow(dead_code)]
    pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,

    /// Origin tracking for simple optimizations (e.g., object.method after new)
    /// Maps a ValueId to the class name if it was produced by NewBox of that class
    pub(super) value_origin_newbox: HashMap<ValueId, String>,

    /// Names of user-defined boxes declared in the current module
    pub(super) user_defined_boxes: HashSet<String>,

    /// Weak field registry: BoxName -> {weak field names}
    pub(super) weak_fields_by_box: HashMap<String, HashSet<String>>,

    /// Remember class of object fields after assignments: (base_id, field) -> class_name
    pub(super) field_origin_class: HashMap<(ValueId, String), String>,

    /// Optional per-value type annotations (MIR-level): ValueId -> MirType
    pub(super) value_types: HashMap<ValueId, super::MirType>,
    /// Current static box name when lowering a static box body (e.g., "Main")
    current_static_box: Option<String>,

    /// Include guards: currently loading file canonical paths
    include_loading: HashSet<String>,
    /// Include visited cache: canonical path -> box name
    include_box_map: HashMap<String, String>,
}

impl MirBuilder {
    /// Emit a Box method call (PluginInvoke unified)
    fn emit_box_or_plugin_call(
        &mut self,
        dst: Option<ValueId>,
        box_val: ValueId,
        method: String,
        method_id: Option<u16>,
        args: Vec<ValueId>,
        effects: EffectMask,
    ) -> Result<(), String> {
        let _ = method_id; // slot is no longer used in unified plugin invoke path
        self.emit_instruction(MirInstruction::PluginInvoke { dst, box_val, method, args, effects })
    }
    /// Create a new MIR builder
    pub fn new() -> Self {
        Self {
            current_module: None,
            current_function: None,
            current_block: None,
            value_gen: ValueIdGenerator::new(),
            block_gen: BasicBlockIdGenerator::new(),
            variable_map: HashMap::new(),
            pending_phis: Vec::new(),
            value_origin_newbox: HashMap::new(),
            user_defined_boxes: HashSet::new(),
            weak_fields_by_box: HashMap::new(),
            field_origin_class: HashMap::new(),
            value_types: HashMap::new(),
            current_static_box: None,
            include_loading: HashSet::new(),
            include_box_map: HashMap::new(),
        }
    }

    /// Emit a type check instruction (Unified: TypeOp(Check))
    #[allow(dead_code)]
    pub(super) fn emit_type_check(&mut self, value: ValueId, expected_type: String) -> Result<ValueId, String> {
        let dst = self.value_gen.next();
        self.emit_instruction(MirInstruction::TypeOp { dst, op: super::TypeOpKind::Check, value, ty: super::MirType::Box(expected_type) })?;
        Ok(dst)
    }

    /// Emit a cast instruction (Unified: TypeOp(Cast))
    #[allow(dead_code)]
    pub(super) fn emit_cast(&mut self, value: ValueId, target_type: super::MirType) -> Result<ValueId, String> {
        let dst = self.value_gen.next();
        self.emit_instruction(MirInstruction::TypeOp { dst, op: super::TypeOpKind::Cast, value, ty: target_type.clone() })?;
        Ok(dst)
    }

    /// Emit a weak reference creation (Unified: WeakRef(New))
    #[allow(dead_code)]
    pub(super) fn emit_weak_new(&mut self, box_val: ValueId) -> Result<ValueId, String> {
        let dst = self.value_gen.next();
        self.emit_instruction(MirInstruction::WeakRef { dst, op: super::WeakRefOp::New, value: box_val })?;
        Ok(dst)
    }

    /// Emit a weak reference load (Unified: WeakRef(Load))
    #[allow(dead_code)]
    pub(super) fn emit_weak_load(&mut self, weak_ref: ValueId) -> Result<ValueId, String> {
        let dst = self.value_gen.next();
        self.emit_instruction(MirInstruction::WeakRef { dst, op: super::WeakRefOp::Load, value: weak_ref })?;
        Ok(dst)
    }

    /// Emit a barrier read (Unified: Barrier(Read))
    #[allow(dead_code)]
    pub(super) fn emit_barrier_read(&mut self, ptr: ValueId) -> Result<(), String> {
        self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Read, ptr })
    }

    /// Emit a barrier write (Unified: Barrier(Write))
    #[allow(dead_code)]
    pub(super) fn emit_barrier_write(&mut self, ptr: ValueId) -> Result<(), String> {
        self.emit_instruction(MirInstruction::Barrier { op: super::BarrierOp::Write, ptr })
    }

    // moved to builder_calls.rs: lower_method_as_function
    
    /// Build a complete MIR module from AST
    pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
        // Create a new module
        let module = MirModule::new("main".to_string());
        
        // Create a main function to contain the AST
        let main_signature = FunctionSignature {
            name: "main".to_string(),
            params: vec![],
            return_type: MirType::Void,
            effects: EffectMask::PURE,
        };
        
        let entry_block = self.block_gen.next();
        let mut main_function = MirFunction::new(main_signature, entry_block);
        main_function.metadata.is_entry_point = true;
        
        // Set up building context
        self.current_module = Some(module);
        self.current_function = Some(main_function);
        self.current_block = Some(entry_block);
        
        // Optional: Add safepoint at function entry (disabled by default)
        if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY").ok().as_deref() == Some("1") {
            self.emit_instruction(MirInstruction::Safepoint)?;
        }
        
        // Convert AST to MIR
        let result_value = self.build_expression(ast)?;
        
        // Add return instruction if needed
        if let Some(block_id) = self.current_block {
            if let Some(ref mut function) = self.current_function {
                if let Some(block) = function.get_block_mut(block_id) {
                    if !block.is_terminated() {
                        block.add_instruction(MirInstruction::Return {
                            value: Some(result_value),
                        });
                    }
                    // Infer return type from TyEnv (value_types)
                    if let Some(mt) = self.value_types.get(&result_value).cloned() {
                        function.signature.return_type = mt;
                    }
                }
            }
        }
        
        // Finalize and return module
        let mut module = self.current_module.take().unwrap();
        let mut function = self.current_function.take().unwrap();
        // Flush value_types (TyEnv) into function metadata
        function.metadata.value_types = self.value_types.clone();
        // 補助: 本文中に明示的な return <expr> が存在する場合、
        // 末尾returnを挿入しなかった関数でも戻り型を推定する。
        if matches!(function.signature.return_type, super::MirType::Void | super::MirType::Unknown) {
            let mut inferred: Option<super::MirType> = None;
            'outer: for (_bid, bb) in function.blocks.iter() {
                for inst in bb.instructions.iter() {
                    if let super::MirInstruction::Return { value: Some(v) } = inst {
                        if let Some(mt) = self.value_types.get(v).cloned() { inferred = Some(mt); break 'outer; }
                        // 追加: v が PHI の場合は入力側の型から推定
                if let Some(mt) = utils::infer_type_from_phi(&function, *v, &self.value_types) { inferred = Some(mt); break 'outer; }
                    }
                }
                if let Some(super::MirInstruction::Return { value: Some(v) }) = &bb.terminator {
                    if let Some(mt) = self.value_types.get(v).cloned() { inferred = Some(mt); break; }
                    if let Some(mt) = utils::infer_type_from_phi(&function, *v, &self.value_types) { inferred = Some(mt); break; }
                }
            }
            if let Some(mt) = inferred { function.signature.return_type = mt; }
        }
        module.add_function(function);

        
        Ok(module)
    }
    
    /// Build an expression and return its value ID
    pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result<ValueId, String> {
        match ast {
            ASTNode::Literal { value, .. } => {
                self.build_literal(value)
            },
            
            ASTNode::BinaryOp { left, operator, right, .. } => {
                self.build_binary_op(*left, operator, *right)
            },
            
            ASTNode::UnaryOp { operator, operand, .. } => {
                let op_string = match operator {
                    crate::ast::UnaryOperator::Minus => "-".to_string(),
                    crate::ast::UnaryOperator::Not => "not".to_string(),
                };
                self.build_unary_op(op_string, *operand)
            },
            
            ASTNode::Variable { name, .. } => {
                self.build_variable_access(name.clone())
            },
            
            ASTNode::Me { .. } => {
                self.build_me_expression()
            },
            
            ASTNode::MethodCall { object, method, arguments, .. } => {
                // Early TypeOp lowering for method-style is()/as()
                if (method == "is" || method == "as") && arguments.len() == 1 {
                    if let Some(type_name) = Self::extract_string_literal(&arguments[0]) {
                        let obj_val = self.build_expression(*object.clone())?;
                        let ty = Self::parse_type_name_to_mir(&type_name);
                        let dst = self.value_gen.next();
                        let op = if method == "is" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
                        self.emit_instruction(MirInstruction::TypeOp { dst, op, value: obj_val, ty })?;
                        return Ok(dst);
                    }
                }
                self.build_method_call(*object.clone(), method.clone(), arguments.clone())
            },
            
            ASTNode::FromCall { parent, method, arguments, .. } => {
                self.build_from_expression(parent.clone(), method.clone(), arguments.clone())
            },
            
            ASTNode::Assignment { target, value, .. } => {
                // Check if target is a field access for RefSet
                if let ASTNode::FieldAccess { object, field, .. } = target.as_ref() {
                    self.build_field_assignment(*object.clone(), field.clone(), *value.clone())
                } else if let ASTNode::Variable { name, .. } = target.as_ref() {
                    // Plain variable assignment - existing behavior
                    self.build_assignment(name.clone(), *value.clone())
                } else {
                    Err("Complex assignment targets not yet supported in MIR".to_string())
                }
            },
            
            ASTNode::FunctionCall { name, arguments, .. } => {
                // Early TypeOp lowering for function-style isType()/asType()
                if (name == "isType" || name == "asType") && arguments.len() == 2 {
                    if let Some(type_name) = Self::extract_string_literal(&arguments[1]) {
                        let val = self.build_expression(arguments[0].clone())?;
                        let ty = Self::parse_type_name_to_mir(&type_name);
                        let dst = self.value_gen.next();
                        let op = if name == "isType" { super::TypeOpKind::Check } else { super::TypeOpKind::Cast };
                        self.emit_instruction(MirInstruction::TypeOp { dst, op, value: val, ty })?;
                        return Ok(dst);
                    }
                }
                self.build_function_call(name.clone(), arguments.clone())
            },
            
            ASTNode::Print { expression, .. } => {
                self.build_print_statement(*expression.clone())
            },
            
            ASTNode::Program { statements, .. } => {
                self.build_block(statements.clone())
            },
            
            ASTNode::If { condition, then_body, else_body, .. } => {
                let else_ast = if let Some(else_statements) = else_body {
                    Some(ASTNode::Program {
                        statements: else_statements.clone(),
                        span: crate::ast::Span::unknown(),
                    })
                } else {
                    None
                };
                
                self.build_if_statement(
                    *condition.clone(), 
                    ASTNode::Program {
                        statements: then_body.clone(),
                        span: crate::ast::Span::unknown(),
                    },
                    else_ast
                )
            },
            
            ASTNode::Loop { condition, body, .. } => {
                self.build_loop_statement(*condition.clone(), body.clone())
            },
            
            ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => {
                self.build_try_catch_statement(try_body.clone(), catch_clauses.clone(), finally_body.clone())
            },
            
            ASTNode::Throw { expression, .. } => {
                self.build_throw_statement(*expression.clone())
            },
            
            ASTNode::Return { value, .. } => {
                self.build_return_statement(value.clone())
            },
            
            ASTNode::Local { variables, initial_values, .. } => {
                self.build_local_statement(variables.clone(), initial_values.clone())
            },
            
            ASTNode::BoxDeclaration { name, methods, is_static, fields, constructors, weak_fields, .. } => {
                if is_static && name == "Main" {
                    self.build_static_main_box(name.clone(), methods.clone())
                } else {
                    // Support user-defined boxes - handle as statement, return void
                    // Track as user-defined (eligible for method lowering)
                    self.user_defined_boxes.insert(name.clone());
                    self.build_box_declaration(name.clone(), methods.clone(), fields.clone(), weak_fields.clone())?;

                    // Phase 2: Lower constructors (birth/N) into MIR functions
                    // Function name pattern: "{BoxName}.{constructor_key}" (e.g., "Person.birth/1")
                    for (ctor_key, ctor_ast) in constructors.clone() {
                        if let ASTNode::FunctionDeclaration { params, body, .. } = ctor_ast {
                            let func_name = format!("{}.{}", name, ctor_key);
                            self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?;
                        }
                    }

                    // Phase 3: Lower instance methods into MIR functions
                    // Function name pattern: "{BoxName}.{method}/{N}"
                    for (method_name, method_ast) in methods.clone() {
                        if let ASTNode::FunctionDeclaration { params, body, is_static, .. } = method_ast {
                            if !is_static {
                                let func_name = format!("{}.{}{}", name, method_name, format!("/{}", params.len()));
                                self.lower_method_as_function(func_name, name.clone(), params.clone(), body.clone())?;
                            }
                        }
                    }
                    
                    // Return a void value since this is a statement
                    let void_val = self.value_gen.next();
                    self.emit_instruction(MirInstruction::Const {
                        dst: void_val,
                        value: ConstValue::Void,
                    })?;
                    Ok(void_val)
                }
            },
            
            ASTNode::FieldAccess { object, field, .. } => {
                self.build_field_access(*object.clone(), field.clone())
            },
            
            ASTNode::New { class, arguments, .. } => {
                self.build_new_expression(class.clone(), arguments.clone())
            },
            
            // Phase 7: Async operations
            ASTNode::Nowait { variable, expression, .. } => {
                self.build_nowait_statement(variable.clone(), *expression.clone())
            },
            
            ASTNode::AwaitExpression { expression, .. } => {
                self.build_await_expression(*expression.clone())
            },
            
            ASTNode::Include { filename, .. } => {
                // Resolve and read included file
                let mut path = utils::resolve_include_path_builder(&filename);
                if std::path::Path::new(&path).is_dir() {
                    path = format!("{}/index.nyash", path.trim_end_matches('/'));
                } else if std::path::Path::new(&path).extension().is_none() {
                    path.push_str(".nyash");
                }
                // Cycle detection
                if self.include_loading.contains(&path) {
                    return Err(format!("Circular include detected: {}", path));
                }
                // Cache hit: build only the instance
                if let Some(name) = self.include_box_map.get(&path).cloned() {
                    return self.build_new_expression(name, vec![]);
                }
                self.include_loading.insert(path.clone());
                let content = fs::read_to_string(&path)
                    .map_err(|e| format!("Include read error '{}': {}", filename, e))?;
                // Parse to AST
                let included_ast = crate::parser::NyashParser::parse_from_string(&content)
                    .map_err(|e| format!("Include parse error '{}': {:?}", filename, e))?;
                // Find first static box name
                let mut box_name: Option<String> = None;
                if let crate::ast::ASTNode::Program { statements, .. } = &included_ast {
                    for st in statements {
                        if let crate::ast::ASTNode::BoxDeclaration { name, is_static, .. } = st {
                            if *is_static { box_name = Some(name.clone()); break; }
                        }
                    }
                }
                let bname = box_name.ok_or_else(|| format!("Include target '{}' has no static box", filename))?;
                // Lower included AST into current MIR (register types/methods)
                let _ = self.build_expression(included_ast)?;
                // Mark caches
                self.include_loading.remove(&path);
                self.include_box_map.insert(path.clone(), bname.clone());
                // Return a new instance of included box (no args)
                self.build_new_expression(bname, vec![])
            },
            
            _ => {
                Err(format!("Unsupported AST node type: {:?}", ast))
            }
        }
    }
    
    /// Build a literal value
    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,
        };

        let const_value = match literal {
            LiteralValue::Integer(n) => ConstValue::Integer(n),
            LiteralValue::Float(f) => ConstValue::Float(f),
            LiteralValue::String(s) => ConstValue::String(s),
            LiteralValue::Bool(b) => ConstValue::Bool(b),
            LiteralValue::Null => ConstValue::Null,
            LiteralValue::Void => ConstValue::Void,
        };
        
        let dst = self.value_gen.next();
        self.emit_instruction(MirInstruction::Const {
            dst,
            value: const_value,
        })?;
        // Annotate type
        if let Some(ty) = ty_for_dst { self.value_types.insert(dst, ty); }
        
        Ok(dst)
    }
    
    // build_binary_op moved to builder/ops.rs
    // build_unary_op moved to builder/ops.rs
    
    /// Build variable access
    fn build_variable_access(&mut self, name: String) -> Result<ValueId, String> {
        if let Some(&value_id) = self.variable_map.get(&name) {
            Ok(value_id)
        } else {
            Err(format!("Undefined variable: {}", name))
        }
    }
    
    /// Build assignment
    fn build_assignment(&mut self, var_name: String, value: ASTNode) -> Result<ValueId, String> {
        let value_id = self.build_expression(value)?;
        
        // In SSA form, each assignment creates a new value
        self.variable_map.insert(var_name.clone(), value_id);
        
        Ok(value_id)
    }
    
    // build_function_call_legacy removed (use builder_calls::build_function_call)
    
    // build_print_statement_legacy moved to builder/stmts.rs
    
    // build_block_legacy moved to builder/stmts.rs
    
    // build_if_statement_legacy moved to builder/stmts.rs

    // extract_assigned_var moved to builder/stmts.rs (as module helper)
    
    /// 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")?;
        
        if let Some(ref mut function) = self.current_function {
            if let Some(block) = function.get_block_mut(block_id) {
                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),
                    });
                }
                block.add_instruction(instruction);
                Ok(())
            } else {
                Err(format!("Basic block {} does not exist", block_id))
            }
        } else {
            Err("No current function".to_string())
        }
    }
    
    /// Ensure a basic block exists in the current function
    pub(super) fn ensure_block_exists(&mut self, block_id: BasicBlockId) -> Result<(), String> {
        if let Some(ref mut function) = self.current_function {
            if !function.blocks.contains_key(&block_id) {
                let block = BasicBlock::new(block_id);
                function.add_block(block);
            }
            Ok(())
        } else {
            Err("No current function".to_string())
        }
    }
    
    // build_loop_statement_legacy moved to builder/stmts.rs
    
    // build_try_catch_statement_legacy moved to builder/stmts.rs
    
    // build_throw_statement_legacy moved to builder/stmts.rs
    
    // build_local_statement_legacy moved to builder/stmts.rs
    
    // build_return_statement_legacy moved to builder/stmts.rs
    
    /// Build static box (e.g., Main) - extracts main() method body and converts to Program
    /// Also lowers other static methods into standalone MIR functions: BoxName.method/N
    fn build_static_main_box(&mut self, box_name: String, methods: std::collections::HashMap<String, ASTNode>) -> Result<ValueId, String> {
        // Lower other static methods (except main) to standalone MIR functions so JIT can see them
        for (mname, mast) in methods.iter() {
            if mname == "main" { continue; }
            if let ASTNode::FunctionDeclaration { params, body, .. } = mast {
                let func_name = format!("{}.{}{}", box_name, mname, format!("/{}", params.len()));
                self.lower_static_method_as_function(func_name, params.clone(), body.clone())?;
            }
        }
        // Within this lowering, treat `me` receiver as this static box
        let saved_static = self.current_static_box.clone();
        self.current_static_box = Some(box_name.clone());
        // Look for the main() method
        let out = if let Some(main_method) = methods.get("main") {
            if let ASTNode::FunctionDeclaration { params, body, .. } = main_method {
                // Convert the method body to a Program AST node and lower it
                let program_ast = ASTNode::Program {
                    statements: body.clone(),
                    span: crate::ast::Span::unknown(),
                };
                
                // Bind default parameters if present (e.g., args=[])
                // Save current var map; inject defaults; restore after lowering
                let saved_var_map = std::mem::take(&mut self.variable_map);
                // Prepare defaults for known patterns
                for p in params.iter() {
                    let pid = self.value_gen.next();
                    // Heuristic: for parameter named "args", create new ArrayBox(); else use Void
                    if p == "args" {
                        // new ArrayBox() -> pid
                        // Emit NewBox for ArrayBox with no args
                        self.emit_instruction(MirInstruction::NewBox { dst: pid, box_type: "ArrayBox".to_string(), args: vec![] })?;
                    } else {
                        self.emit_instruction(MirInstruction::Const { dst: pid, value: ConstValue::Void })?;
                    }
                    self.variable_map.insert(p.clone(), pid);
                }
                // Use existing Program lowering logic
                let lowered = self.build_expression(program_ast);
                // Restore variable map
                self.variable_map = saved_var_map;
                lowered
            } else {
                Err("main method in static box is not a FunctionDeclaration".to_string())
            }
        } else {
            Err("static box must contain a main() method".to_string())
        };
        // Restore static box context
        self.current_static_box = saved_static;
        out
    }
    
    /// Build field access: object.field
    fn build_field_access(&mut self, object: ASTNode, field: String) -> Result<ValueId, String> {
        // Clone the object before building expression if we need to check it later
        let object_clone = object.clone();
        
        // First, build the object expression to get its ValueId
        let object_value = self.build_expression(object)?;

        // Get the field from the object using RefGet
        let field_val = self.value_gen.next();
        self.emit_instruction(MirInstruction::RefGet {
            dst: field_val,
            reference: object_value,
            field: field.clone(),
        })?;

        // If we recorded origin class for this field on this base object, propagate it to this value id
        if let Some(class_name) = self.field_origin_class.get(&(object_value, field.clone())).cloned() {
            self.value_origin_newbox.insert(field_val, class_name);
        }

        // If we can infer the box type and the field is weak, emit WeakLoad (+ optional barrier)
        let mut inferred_class: Option<String> = self.value_origin_newbox.get(&object_value).cloned();
        // Fallback: if the object is a nested field access like (X.Y).Z, consult recorded field origins for X.Y
        if inferred_class.is_none() {
            if let ASTNode::FieldAccess { object: inner_obj, field: ref parent_field, .. } = object_clone {
                // Build inner base to get a stable id and consult mapping
                if let Ok(base_id) = self.build_expression(*inner_obj.clone()) {
                    if let Some(cls) = self.field_origin_class.get(&(base_id, parent_field.clone())) {
                        inferred_class = Some(cls.clone());
                    }
                }
            }
        }
        if let Some(class_name) = inferred_class {
            if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
                if weak_set.contains(&field) {
                    // Barrier (read) PoC
                    let _ = self.emit_barrier_read(field_val);
                    // WeakLoad
                    let loaded = self.emit_weak_load(field_val)?;
                    return Ok(loaded);
                }
            }
        }

        Ok(field_val)
    }
    
    /// Build new expression: new ClassName(arguments)
    fn build_new_expression(&mut self, class: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
        // Phase 9.78a: Unified Box creation using NewBox instruction
        
        // 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() {
                let dst = self.value_gen.next();
                self.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(n) })?;
                self.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
        let dst = self.value_gen.next();
        
        // 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(),
        })?;
        // Annotate primitive boxes
        match class.as_str() {
            "IntegerBox" => { self.value_types.insert(dst, super::MirType::Integer); },
            "FloatBox" => { self.value_types.insert(dst, super::MirType::Float); },
            "BoolBox" => { self.value_types.insert(dst, super::MirType::Bool); },
            "StringBox" => { self.value_types.insert(dst, super::MirType::String); },
            other => { self.value_types.insert(dst, super::MirType::Box(other.to_string())); }
        }

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

        // For plugin/builtin boxes, call birth(...). For user-defined boxes, skip (InstanceBox already constructed)
        if !self.user_defined_boxes.contains(&class) {
            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)
    }
    
    /// Build field assignment: object.field = value
    fn build_field_assignment(&mut self, object: ASTNode, field: String, value: ASTNode) -> Result<ValueId, String> {
        // Build the object and value expressions
        let object_value = self.build_expression(object)?;
        let mut value_result = self.build_expression(value)?;

        // If we can infer the box type and the field is weak, create WeakRef before store
        if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
            if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
                if weak_set.contains(&field) {
                    value_result = self.emit_weak_new(value_result)?;
                }
            }
        }

        // Set the field using RefSet
        self.emit_instruction(MirInstruction::RefSet {
            reference: object_value,
            field: field.clone(),
            value: value_result,
        })?;

        // Emit a write barrier for weak fields (PoC)
        if let Some(class_name) = self.value_origin_newbox.get(&object_value).cloned() {
            if let Some(weak_set) = self.weak_fields_by_box.get(&class_name) {
                if weak_set.contains(&field) {
                    let _ = self.emit_barrier_write(value_result);
                }
            }
        }

        // Record origin class for this field (if the value originates from NewBox of a known class)
        if let Some(class_name) = self.value_origin_newbox.get(&value_result).cloned() {
            self.field_origin_class.insert((object_value, field.clone()), class_name);
        }

        // Return the assigned value
        Ok(value_result)
    }
    
    /// Start a new basic block
    pub(super) fn start_new_block(&mut self, block_id: BasicBlockId) -> Result<(), String> {
        if let Some(ref mut function) = self.current_function {
            function.add_block(BasicBlock::new(block_id));
            self.current_block = Some(block_id);
            Ok(())
        } else {
            Err("No current function".to_string())
        }
    }
    
    /// 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.current_function) {
            if let Some(block) = function.get_block(block_id) {
                return block.is_terminated();
            }
        }
        false
    }
    
    // convert_binary_operator moved to builder/ops.rs
    // convert_unary_operator moved to builder/ops.rs
    
    // build_nowait_statement_legacy moved to builder/stmts.rs
    
    // build_await_expression_legacy moved to builder/stmts.rs
    
    // build_me_expression_legacy moved to builder/stmts.rs
    
    // build_method_call_legacy removed (use builder_calls::build_method_call)

    // parse_type_name_to_mir_legacy removed (use builder_calls::parse_type_name_to_mir)
    // extract_string_literal_legacy removed (use builder_calls::extract_string_literal)
    // build_from_expression_legacy removed (use builder_calls::build_from_expression)

    // lower_static_method_as_function_legacy removed (use builder_calls::lower_static_method_as_function)
    
    /// Build box declaration: box Name { fields... methods... }
    fn build_box_declaration(&mut self, name: String, methods: std::collections::HashMap<String, ASTNode>, fields: Vec<String>, weak_fields: Vec<String>) -> Result<(), String> {
        // For Phase 8.4, we'll emit metadata instructions to register the box type
        // In a full implementation, this would register type information for later use
        
        // Create a type registration constant
        let type_id = self.value_gen.next();
        self.emit_instruction(MirInstruction::Const {
            dst: type_id,
            value: ConstValue::String(format!("__box_type_{}", name)),
        })?;
        
        // For each field, emit metadata about the field
        for field in fields {
            let field_id = self.value_gen.next();
            self.emit_instruction(MirInstruction::Const {
                dst: field_id,
                value: ConstValue::String(format!("__field_{}_{}", name, field)),
            })?;
        }

        // Record weak fields for this box
        if !weak_fields.is_empty() {
            let set: HashSet<String> = weak_fields.into_iter().collect();
            self.weak_fields_by_box.insert(name.clone(), set);
        }
        
        // Reserve method slots for user-defined instance methods (deterministic, starts at 4)
        let mut instance_methods: Vec<String> = Vec::new();
        for (mname, mast) in &methods {
            if let ASTNode::FunctionDeclaration { is_static, .. } = mast {
                if !*is_static { instance_methods.push(mname.clone()); }
            }
        }
        instance_methods.sort();
        if !instance_methods.is_empty() {
            let tyid = get_or_assign_type_id(&name);
            for (i, m) in instance_methods.iter().enumerate() {
                let slot = 4u16.saturating_add(i as u16);
                reserve_method_slot(tyid, m, slot);
            }
        }
        
        // Process methods - now methods is a HashMap
        for (method_name, method_ast) in methods {
            if let ASTNode::FunctionDeclaration { .. } = method_ast {
                let method_id = self.value_gen.next();
                self.emit_instruction(MirInstruction::Const {
                    dst: method_id,
                    value: ConstValue::String(format!("__method_{}_{}", name, method_name)),
                })?;
            }
        }
        
        Ok(())
    }
}

// BinaryOpType moved to builder/ops.rs

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

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ast::{ASTNode, LiteralValue, Span};
    
    #[test]
    fn test_literal_building() {
        let mut builder = MirBuilder::new();
        
        let ast = ASTNode::Literal {
            value: LiteralValue::Integer(42),
            span: Span::unknown(),
        };
        
        let result = builder.build_module(ast);
        assert!(result.is_ok());
        
        let module = result.unwrap();
        assert_eq!(module.function_names().len(), 1);
        assert!(module.get_function("main").is_some());
    }
    
    #[test]
    fn test_binary_op_building() {
        let mut builder = MirBuilder::new();
        
        let ast = ASTNode::BinaryOp {
            left: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(10),
                span: Span::unknown(),
            }),
            operator: BinaryOperator::Add,
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(32),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };
        
        let result = builder.build_module(ast);
        assert!(result.is_ok());
        
        let module = result.unwrap();
        let function = module.get_function("main").unwrap();
        
        // Should have constants and binary operation
        let stats = function.stats();
        assert!(stats.instruction_count >= 3); // 2 constants + 1 binop + 1 return
    }
    
    #[test]
    fn test_if_statement_building() {
        let mut builder = MirBuilder::new();
        
        // Adapt test to current AST: If with statement bodies
        let ast = ASTNode::If {
            condition: Box::new(ASTNode::Literal {
                value: LiteralValue::Bool(true),
                span: Span::unknown(),
            }),
            then_body: vec![ASTNode::Literal {
                value: LiteralValue::Integer(1),
                span: Span::unknown(),
            }],
            else_body: Some(vec![ASTNode::Literal {
                value: LiteralValue::Integer(2),
                span: Span::unknown(),
            }]),
            span: Span::unknown(),
        };
        
        let result = builder.build_module(ast);
        assert!(result.is_ok());
        
        let module = result.unwrap();
        let function = module.get_function("main").unwrap();
        
        // Should have multiple blocks for if/then/else/merge
        assert!(function.blocks.len() >= 3);
        
        // Should have phi function in merge block
        let stats = function.stats();
        assert!(stats.phi_count >= 1);
    }
}
