use super::{origin, observe, utils};
use super::{BasicBlockId, MirBuilder, MirInstruction, ValueId};

impl MirBuilder {
    /// Emit an instruction to the current basic block
    pub(in crate::mir) 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())
        }
    }
}
