//! Phase 176-2 / Phase 179 / Phase 184: Carrier Update Emission
//!
//! Converts UpdateExpr (from LoopUpdateAnalyzer) into JoinIR instructions
//! that compute the updated carrier value.
//!
//! This module is extracted from loop_with_break_minimal.rs to improve
//! modularity and single responsibility.
//!
//! Phase 184: Added UpdateEnv support for body-local variable resolution.

use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs};
use crate::mir::join_ir::lowering::update_env::UpdateEnv;
use crate::mir::join_ir::{BinOpKind, ConstValue, JoinInst, MirLikeInst};
use crate::mir::ValueId;

/// Emit JoinIR instructions for a single carrier update (Phase 184: UpdateEnv version)
///
/// Converts UpdateExpr (from LoopUpdateAnalyzer) into JoinIR instructions
/// that compute the updated carrier value. Supports both condition variables
/// and body-local variables through UpdateEnv.
///
/// # Arguments
///
/// * `carrier` - Carrier variable information (name, ValueId)
/// * `update` - Update expression (e.g., CounterLike, AccumulationLike)
/// * `alloc_value` - ValueId allocator closure
/// * `env` - UpdateEnv for unified variable resolution
/// * `instructions` - Output vector to append instructions to
///
/// # Returns
///
/// ValueId of the computed update result
///
/// # Example
///
/// ```ignore
/// // For "count = count + temp":
/// let count_next = emit_carrier_update_with_env(
///     &count_carrier,
///     &UpdateExpr::BinOp { lhs: "count", op: Add, rhs: Variable("temp") },
///     &mut alloc_value,
///     &update_env,  // Has both condition and body-local vars
///     &mut instructions,
/// )?;
/// // Generates:
/// //   count_next = BinOp(Add, count_param, temp_value)
/// ```
pub fn emit_carrier_update_with_env(
    carrier: &CarrierVar,
    update: &UpdateExpr,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &UpdateEnv,
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    match update {
        UpdateExpr::Const(step) => {
            // CounterLike: carrier = carrier + step
            // Allocate const ValueId
            let const_id = alloc_value();
            instructions.push(JoinInst::Compute(MirLikeInst::Const {
                dst: const_id,
                value: ConstValue::Integer(*step),
            }));

            // Get carrier parameter ValueId from env
            let carrier_param = env
                .resolve(&carrier.name)
                .ok_or_else(|| format!("Carrier '{}' not found in UpdateEnv", carrier.name))?;

            // Allocate result ValueId
            let result = alloc_value();
            instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
                dst: result,
                op: BinOpKind::Add,
                lhs: carrier_param,
                rhs: const_id,
            }));

            Ok(result)
        }

        UpdateExpr::BinOp { lhs, op, rhs } => {
            // General binary operation: carrier = carrier op rhs
            // Verify lhs matches carrier name
            if lhs != &carrier.name {
                return Err(format!(
                    "Update expression LHS '{}' doesn't match carrier '{}'",
                    lhs, carrier.name
                ));
            }

            // Get carrier parameter ValueId from env
            let carrier_param = env
                .resolve(&carrier.name)
                .ok_or_else(|| format!("Carrier '{}' not found in UpdateEnv", carrier.name))?;

            // Resolve RHS (Phase 184: Now supports body-local variables!)
            let rhs_id = match rhs {
                UpdateRhs::Const(n) => {
                    let const_id = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::Const {
                        dst: const_id,
                        value: ConstValue::Integer(*n),
                    }));
                    const_id
                }
                UpdateRhs::Variable(var_name) => {
                    env.resolve(var_name).ok_or_else(|| {
                        format!(
                            "Update RHS variable '{}' not found in UpdateEnv (neither condition nor body-local)",
                            var_name
                        )
                    })?
                }
                // Phase 188: String updates now emit JoinIR BinOp
                // StringAppendLiteral: s = s + "literal"
                UpdateRhs::StringLiteral(s) => {
                    let const_id = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::Const {
                        dst: const_id,
                        value: ConstValue::String(s.clone()),
                    }));
                    const_id
                }
                // Phase 190: Number accumulation pattern: result = result * base + digit
                // Emit as: tmp = carrier * base; result = tmp + digit
                UpdateRhs::NumberAccumulation { base, digit_var } => {
                    // Step 1: Emit const for base
                    let base_id = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::Const {
                        dst: base_id,
                        value: ConstValue::Integer(*base),
                    }));

                    // Step 2: Emit multiplication: tmp = carrier * base
                    let tmp_id = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
                        dst: tmp_id,
                        op: BinOpKind::Mul,
                        lhs: carrier_param,
                        rhs: base_id,
                    }));

                    // Step 3: Resolve digit variable
                    let digit_id = env.resolve(digit_var).ok_or_else(|| {
                        format!(
                            "Number accumulation digit variable '{}' not found in UpdateEnv",
                            digit_var
                        )
                    })?;

                    // Step 4: Emit addition: result = tmp + digit
                    // This will be handled by the outer BinOp emission
                    // For now, return digit_id to be used as RHS
                    // We need to handle this specially - return tmp_id instead
                    // and adjust the outer BinOp to use correct values

                    // Actually, we need to emit both operations here
                    // Final result = tmp + digit
                    let result = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
                        dst: result,
                        op: *op, // Use the operation from outer UpdateExpr
                        lhs: tmp_id,
                        rhs: digit_id,
                    }));

                    // Return result directly - we've already emitted everything
                    return Ok(result);
                }
                // Phase 178/188: Complex updates (method calls) still rejected
                UpdateRhs::Other => {
                    return Err(format!(
                        "Carrier '{}' has complex update (UpdateRhs::Other) - should be rejected by can_lower()",
                        carrier.name
                    ));
                }
            };

            // Allocate result ValueId
            let result = alloc_value();
            instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
                dst: result,
                op: *op,
                lhs: carrier_param,
                rhs: rhs_id,
            }));

            Ok(result)
        }
    }
}

/// Emit JoinIR instructions for a single carrier update (backward compatibility version)
///
/// This function is kept for backward compatibility with existing Pattern2/4 code
/// that only needs ConditionEnv. New code should prefer `emit_carrier_update_with_env`.
///
/// # Arguments
///
/// * `carrier` - Carrier variable information (name, ValueId)
/// * `update` - Update expression (e.g., CounterLike, AccumulationLike)
/// * `alloc_value` - ValueId allocator closure
/// * `env` - ConditionEnv for variable resolution
/// * `instructions` - Output vector to append instructions to
///
/// # Returns
///
/// ValueId of the computed update result
///
/// # Example
///
/// ```ignore
/// // For "count = count + 1":
/// let count_next = emit_carrier_update(
///     &count_carrier,
///     &UpdateExpr::BinOp { lhs: "count", op: Add, rhs: Const(1) },
///     &mut alloc_value,
///     &env,
///     &mut instructions,
/// )?;
/// // Generates:
/// //   const_1 = Const(1)
/// //   count_next = BinOp(Add, count_param, const_1)
/// ```
pub fn emit_carrier_update(
    carrier: &CarrierVar,
    update: &UpdateExpr,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    match update {
        UpdateExpr::Const(step) => {
            // CounterLike: carrier = carrier + step
            // Allocate const ValueId
            let const_id = alloc_value();
            instructions.push(JoinInst::Compute(MirLikeInst::Const {
                dst: const_id,
                value: ConstValue::Integer(*step),
            }));

            // Get carrier parameter ValueId from env
            let carrier_param = env
                .get(&carrier.name)
                .ok_or_else(|| format!("Carrier '{}' not found in ConditionEnv", carrier.name))?;

            // Allocate result ValueId
            let result = alloc_value();
            instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
                dst: result,
                op: BinOpKind::Add,
                lhs: carrier_param,
                rhs: const_id,
            }));

            Ok(result)
        }

        UpdateExpr::BinOp { lhs, op, rhs } => {
            // General binary operation: carrier = carrier op rhs
            // Verify lhs matches carrier name
            if lhs != &carrier.name {
                return Err(format!(
                    "Update expression LHS '{}' doesn't match carrier '{}'",
                    lhs, carrier.name
                ));
            }

            // Get carrier parameter ValueId from env
            let carrier_param = env
                .get(&carrier.name)
                .ok_or_else(|| format!("Carrier '{}' not found in ConditionEnv", carrier.name))?;

            // Resolve RHS
            let rhs_id = match rhs {
                UpdateRhs::Const(n) => {
                    let const_id = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::Const {
                        dst: const_id,
                        value: ConstValue::Integer(*n),
                    }));
                    const_id
                }
                UpdateRhs::Variable(var_name) => env.get(var_name).ok_or_else(|| {
                    format!(
                        "Update RHS variable '{}' not found in ConditionEnv",
                        var_name
                    )
                })?,
                // Phase 188: String updates now emit JoinIR BinOp
                // StringAppendLiteral: s = s + "literal"
                UpdateRhs::StringLiteral(s) => {
                    let const_id = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::Const {
                        dst: const_id,
                        value: ConstValue::String(s.clone()),
                    }));
                    const_id
                }
                // Phase 190: Number accumulation pattern: result = result * base + digit
                // Emit as: tmp = carrier * base; result = tmp + digit
                UpdateRhs::NumberAccumulation { base, digit_var } => {
                    // Step 1: Emit const for base
                    let base_id = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::Const {
                        dst: base_id,
                        value: ConstValue::Integer(*base),
                    }));

                    // Step 2: Emit multiplication: tmp = carrier * base
                    let tmp_id = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
                        dst: tmp_id,
                        op: BinOpKind::Mul,
                        lhs: carrier_param,
                        rhs: base_id,
                    }));

                    // Step 3: Resolve digit variable
                    let digit_id = env.get(digit_var).ok_or_else(|| {
                        format!(
                            "Number accumulation digit variable '{}' not found in ConditionEnv",
                            digit_var
                        )
                    })?;

                    // Step 4: Emit addition: result = tmp + digit
                    let result = alloc_value();
                    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
                        dst: result,
                        op: *op, // Use the operation from outer UpdateExpr
                        lhs: tmp_id,
                        rhs: digit_id,
                    }));

                    // Return result directly - we've already emitted everything
                    return Ok(result);
                }
                // Phase 178/188: Complex updates (method calls) still rejected
                UpdateRhs::Other => {
                    return Err(format!(
                        "Carrier '{}' has complex update (UpdateRhs::Other) - should be rejected by can_lower()",
                        carrier.name
                    ));
                }
            };

            // Allocate result ValueId
            let result = alloc_value();
            instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
                dst: result,
                op: *op,
                lhs: carrier_param,
                rhs: rhs_id,
            }));

            Ok(result)
        }
    }
}

// ============================================================================
// Phase 92 P0-3: ConditionalStep Support
// ============================================================================

use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::condition_lowerer::lower_condition_to_joinir_no_body_locals;
use crate::mir::join_ir::VarId;
use crate::mir::MirType;

/// Emit JoinIR instructions for conditional step update (Phase 92 P0-3)
///
/// Handles the P5b escape sequence pattern where carrier update depends on a condition:
/// ```text
/// if escape_cond { carrier = carrier + then_delta }
/// else { carrier = carrier + else_delta }
/// ```
///
/// This generates:
/// 1. Lower condition expression to get cond_id
/// 2. Compute then_result = carrier + then_delta
/// 3. Compute else_result = carrier + else_delta
/// 4. JoinInst::Select { dst: carrier_new, cond: cond_id, then_val: then_result, else_val: else_result }
///
/// # Arguments
///
/// * `carrier` - Carrier variable information (name, ValueId)
/// * `cond_ast` - AST node for the condition expression (e.g., `ch == '\\'`)
/// * `then_delta` - Delta to add when condition is true
/// * `else_delta` - Delta to add when condition is false
/// * `alloc_value` - ValueId allocator closure
/// * `env` - ConditionEnv for variable resolution
/// * `instructions` - Output vector to append instructions to
///
/// # Returns
///
/// ValueId of the computed update result (the dst of Select)
pub fn emit_conditional_step_update(
    carrier: &CarrierVar,
    cond_ast: &ASTNode,
    then_delta: i64,
    else_delta: i64,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    // Step 1: Lower the condition expression
    // Phase 92 P2-2: No body-local support in legacy emitter (use common/conditional_step_emitter instead)
    let (cond_id, cond_insts) = lower_condition_to_joinir_no_body_locals(cond_ast, alloc_value, env)?;
    instructions.extend(cond_insts);

    // Step 2: Get carrier parameter ValueId from env
    let carrier_param = env
        .get(&carrier.name)
        .ok_or_else(|| format!("Carrier '{}' not found in ConditionEnv", carrier.name))?;

    // Step 3: Compute then_result = carrier + then_delta
    let then_const_id = alloc_value();
    instructions.push(JoinInst::Compute(MirLikeInst::Const {
        dst: then_const_id,
        value: ConstValue::Integer(then_delta),
    }));
    let then_result = alloc_value();
    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
        dst: then_result,
        op: BinOpKind::Add,
        lhs: carrier_param,
        rhs: then_const_id,
    }));

    // Step 4: Compute else_result = carrier + else_delta
    let else_const_id = alloc_value();
    instructions.push(JoinInst::Compute(MirLikeInst::Const {
        dst: else_const_id,
        value: ConstValue::Integer(else_delta),
    }));
    let else_result = alloc_value();
    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
        dst: else_result,
        op: BinOpKind::Add,
        lhs: carrier_param,
        rhs: else_const_id,
    }));

    // Step 5: Emit Select instruction
    let carrier_new: VarId = alloc_value();
    instructions.push(JoinInst::Select {
        dst: carrier_new,
        cond: cond_id,
        then_val: then_result,
        else_val: else_result,
        type_hint: Some(MirType::Integer),  // Carrier is always Integer
    });

    Ok(carrier_new)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
    use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
    use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateRhs;
    use crate::mir::join_ir::BinOpKind;
    use crate::mir::ValueId;

    // Helper: Create a test ConditionEnv
    fn test_env() -> ConditionEnv {
        let mut env = ConditionEnv::new();
        env.insert("count".to_string(), ValueId(10));
        env.insert("sum".to_string(), ValueId(20));
        env.insert("i".to_string(), ValueId(30));
        env
    }

    // Helper: Create a test LoopBodyLocalEnv
    fn test_body_local_env() -> LoopBodyLocalEnv {
        let mut env = LoopBodyLocalEnv::new();
        env.insert("temp".to_string(), ValueId(50));
        env.insert("digit".to_string(), ValueId(60));
        env
    }

    // Helper: Create a test UpdateEnv
    fn test_update_env() -> (ConditionEnv, LoopBodyLocalEnv) {
        (test_env(), test_body_local_env())
    }

    // Helper: Create a test CarrierVar
    fn test_carrier(name: &str, host_id: u32) -> CarrierVar {
        CarrierVar {
            name: name.to_string(),
            host_id: ValueId(host_id),
            join_id: None, // Phase 177-STRUCT-1
            role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
            init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
            #[cfg(feature = "normalized_dev")]
            binding_id: None,
        }
    }

    #[test]
    fn test_emit_const_update() {
        // Test: count = count + 1 (UpdateExpr::Const)
        let carrier = test_carrier("count", 100);
        let update = UpdateExpr::Const(1);
        let env = test_env();

        let mut value_counter = 50u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result =
            emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);

        assert!(result.is_ok());
        let result_id = result.unwrap();

        // Should generate 2 instructions: Const(1) + BinOp(Add)
        assert_eq!(instructions.len(), 2);

        // Instruction 1: Const(1)
        match &instructions[0] {
            JoinInst::Compute(MirLikeInst::Const { dst, value }) => {
                assert_eq!(*dst, ValueId(50)); // First allocated
                assert!(matches!(value, ConstValue::Integer(1)));
            }
            _ => panic!("Expected Const instruction"),
        }

        // Instruction 2: BinOp(Add, count, const_1)
        match &instructions[1] {
            JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
                assert_eq!(*dst, ValueId(51)); // Second allocated
                assert_eq!(*op, BinOpKind::Add);
                assert_eq!(*lhs, ValueId(10)); // count from env
                assert_eq!(*rhs, ValueId(50)); // const_1
            }
            _ => panic!("Expected BinOp instruction"),
        }

        assert_eq!(result_id, ValueId(51));
    }

    #[test]
    fn test_emit_binop_update_with_const() {
        // Test: sum = sum + 5 (UpdateExpr::BinOp with Const RHS)
        let carrier = test_carrier("sum", 200);
        let update = UpdateExpr::BinOp {
            lhs: "sum".to_string(),
            op: BinOpKind::Add,
            rhs: UpdateRhs::Const(5),
        };
        let env = test_env();

        let mut value_counter = 60u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result =
            emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);

        assert!(result.is_ok());
        let result_id = result.unwrap();

        // Should generate 2 instructions: Const(5) + BinOp(Add)
        assert_eq!(instructions.len(), 2);

        // Instruction 1: Const(5)
        match &instructions[0] {
            JoinInst::Compute(MirLikeInst::Const { dst, value }) => {
                assert_eq!(*dst, ValueId(60));
                assert!(matches!(value, ConstValue::Integer(5)));
            }
            _ => panic!("Expected Const instruction"),
        }

        // Instruction 2: BinOp(Add, sum, const_5)
        match &instructions[1] {
            JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
                assert_eq!(*dst, ValueId(61));
                assert_eq!(*op, BinOpKind::Add);
                assert_eq!(*lhs, ValueId(20)); // sum from env
                assert_eq!(*rhs, ValueId(60)); // const_5
            }
            _ => panic!("Expected BinOp instruction"),
        }

        assert_eq!(result_id, ValueId(61));
    }

    #[test]
    fn test_emit_binop_update_with_variable() {
        // Test: sum = sum + i (UpdateExpr::BinOp with Variable RHS)
        let carrier = test_carrier("sum", 200);
        let update = UpdateExpr::BinOp {
            lhs: "sum".to_string(),
            op: BinOpKind::Add,
            rhs: UpdateRhs::Variable("i".to_string()),
        };
        let env = test_env();

        let mut value_counter = 70u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result =
            emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);

        assert!(result.is_ok());
        let result_id = result.unwrap();

        // Should generate 1 instruction: BinOp(Add, sum, i)
        assert_eq!(instructions.len(), 1);

        // Instruction: BinOp(Add, sum, i)
        match &instructions[0] {
            JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
                assert_eq!(*dst, ValueId(70));
                assert_eq!(*op, BinOpKind::Add);
                assert_eq!(*lhs, ValueId(20)); // sum from env
                assert_eq!(*rhs, ValueId(30)); // i from env
            }
            _ => panic!("Expected BinOp instruction"),
        }

        assert_eq!(result_id, ValueId(70));
    }

    #[test]
    fn test_emit_update_carrier_not_in_env() {
        // Test error case: carrier not found in env
        let carrier = test_carrier("unknown", 300);
        let update = UpdateExpr::Const(1);
        let env = test_env(); // doesn't have "unknown"

        let mut value_counter = 80u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result =
            emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);

        assert!(result.is_err());
        assert!(result.unwrap_err().contains("Carrier 'unknown' not found"));
    }

    #[test]
    fn test_emit_update_lhs_mismatch() {
        // Test error case: LHS doesn't match carrier name
        let carrier = test_carrier("count", 100);
        let update = UpdateExpr::BinOp {
            lhs: "sum".to_string(), // Wrong! Should be "count"
            op: BinOpKind::Add,
            rhs: UpdateRhs::Const(1),
        };
        let env = test_env();

        let mut value_counter = 90u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result =
            emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);

        assert!(result.is_err());
        assert!(result.unwrap_err().contains("doesn't match carrier"));
    }

    #[test]
    fn test_emit_update_rhs_variable_not_found() {
        // Test error case: RHS variable not in env
        let carrier = test_carrier("sum", 200);
        let update = UpdateExpr::BinOp {
            lhs: "sum".to_string(),
            op: BinOpKind::Add,
            rhs: UpdateRhs::Variable("unknown_var".to_string()),
        };
        let env = test_env();

        let mut value_counter = 100u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result =
            emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);

        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .contains("Update RHS variable 'unknown_var' not found"));
    }

    // ============================================================================
    // Phase 184: UpdateEnv version tests
    // ============================================================================

    #[test]
    fn test_emit_update_with_env_body_local_variable() {
        // Phase 184: Test using body-local variable in update expression
        // sum = sum + temp (temp is body-local)
        let carrier = test_carrier("sum", 200);
        let update = UpdateExpr::BinOp {
            lhs: "sum".to_string(),
            op: BinOpKind::Add,
            rhs: UpdateRhs::Variable("temp".to_string()), // Body-local variable
        };

        let (cond_env, body_env) = test_update_env();
        let promoted: Vec<String> = vec![];
        let update_env = UpdateEnv::new(&cond_env, &body_env, &promoted);

        let mut value_counter = 110u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result = emit_carrier_update_with_env(
            &carrier,
            &update,
            &mut alloc_value,
            &update_env,
            &mut instructions,
        );

        assert!(result.is_ok());
        let result_id = result.unwrap();

        // Should generate 1 instruction: BinOp(Add, sum, temp)
        assert_eq!(instructions.len(), 1);

        match &instructions[0] {
            JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
                assert_eq!(*dst, ValueId(110));
                assert_eq!(*op, BinOpKind::Add);
                assert_eq!(*lhs, ValueId(20)); // sum from condition env
                assert_eq!(*rhs, ValueId(50)); // temp from body-local env
            }
            _ => panic!("Expected BinOp instruction"),
        }

        assert_eq!(result_id, ValueId(110));
    }

    #[test]
    fn test_emit_update_with_env_condition_priority() {
        // Phase 184: Test condition variable takes priority over body-local
        // If both envs have "x", condition env should win
        let mut cond_env = ConditionEnv::new();
        cond_env.insert("x".to_string(), ValueId(100)); // Condition: x=100
        cond_env.insert("sum".to_string(), ValueId(20));

        let mut body_env = LoopBodyLocalEnv::new();
        body_env.insert("x".to_string(), ValueId(200)); // Body-local: x=200 (should be ignored)

        let promoted: Vec<String> = vec![];
        let update_env = UpdateEnv::new(&cond_env, &body_env, &promoted);

        let carrier = test_carrier("sum", 200);
        let update = UpdateExpr::BinOp {
            lhs: "sum".to_string(),
            op: BinOpKind::Add,
            rhs: UpdateRhs::Variable("x".to_string()),
        };

        let mut value_counter = 120u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result = emit_carrier_update_with_env(
            &carrier,
            &update,
            &mut alloc_value,
            &update_env,
            &mut instructions,
        );

        assert!(result.is_ok());

        // Should use x=100 (condition env), not x=200 (body-local env)
        match &instructions[0] {
            JoinInst::Compute(MirLikeInst::BinOp {
                dst: _,
                op: _,
                lhs: _,
                rhs,
            }) => {
                assert_eq!(*rhs, ValueId(100)); // Condition env wins
            }
            _ => panic!("Expected BinOp instruction"),
        }
    }

    #[test]
    fn test_emit_update_with_env_variable_not_found() {
        // Phase 184: Test error when variable not in either env
        let (cond_env, body_env) = test_update_env();
        let promoted: Vec<String> = vec![];
        let update_env = UpdateEnv::new(&cond_env, &body_env, &promoted);

        let carrier = test_carrier("sum", 200);
        let update = UpdateExpr::BinOp {
            lhs: "sum".to_string(),
            op: BinOpKind::Add,
            rhs: UpdateRhs::Variable("nonexistent".to_string()),
        };

        let mut value_counter = 130u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result = emit_carrier_update_with_env(
            &carrier,
            &update,
            &mut alloc_value,
            &update_env,
            &mut instructions,
        );

        assert!(result.is_err());
        let err = result.unwrap_err();
        assert!(err.contains("Update RHS variable 'nonexistent' not found"));
        assert!(err.contains("neither condition nor body-local"));
    }

    #[test]
    fn test_emit_update_with_env_const_update() {
        // Phase 184: Test UpdateEnv with simple const update (baseline)
        let (cond_env, body_env) = test_update_env();
        let promoted: Vec<String> = vec![];
        let update_env = UpdateEnv::new(&cond_env, &body_env, &promoted);

        let carrier = test_carrier("count", 100);
        let update = UpdateExpr::Const(1);

        let mut value_counter = 140u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result = emit_carrier_update_with_env(
            &carrier,
            &update,
            &mut alloc_value,
            &update_env,
            &mut instructions,
        );

        assert!(result.is_ok());
        assert_eq!(instructions.len(), 2); // Const + BinOp
    }

    #[test]
    fn test_emit_number_accumulation_base10() {
        // Phase 190: Test number accumulation pattern: result = result * 10 + digit
        let mut cond_env = ConditionEnv::new();
        cond_env.insert("result".to_string(), ValueId(20)); // Carrier parameter
        cond_env.insert("digit".to_string(), ValueId(30)); // Digit variable

        let body_env = LoopBodyLocalEnv::new();
        let promoted: Vec<String> = vec![];
        let update_env = UpdateEnv::new(&cond_env, &body_env, &promoted);

        let carrier = test_carrier("result", 200);
        let update = UpdateExpr::BinOp {
            lhs: "result".to_string(),
            op: BinOpKind::Add,
            rhs: UpdateRhs::NumberAccumulation {
                base: 10,
                digit_var: "digit".to_string(),
            },
        };

        let mut value_counter = 150u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result = emit_carrier_update_with_env(
            &carrier,
            &update,
            &mut alloc_value,
            &update_env,
            &mut instructions,
        );

        assert!(result.is_ok());
        let result_id = result.unwrap();

        // Should generate 3 instructions:
        // 1. Const(10) for base
        // 2. BinOp(Mul, result, base) for tmp
        // 3. BinOp(Add, tmp, digit) for final result
        assert_eq!(instructions.len(), 3);

        // Instruction 1: Const(10)
        match &instructions[0] {
            JoinInst::Compute(MirLikeInst::Const { dst, value }) => {
                assert_eq!(*dst, ValueId(150)); // First allocated
                assert!(matches!(value, ConstValue::Integer(10)));
            }
            _ => panic!("Expected Const instruction"),
        }

        // Instruction 2: BinOp(Mul, result, base)
        match &instructions[1] {
            JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
                assert_eq!(*dst, ValueId(151)); // Second allocated (tmp)
                assert_eq!(*op, BinOpKind::Mul);
                assert_eq!(*lhs, ValueId(20)); // result from env
                assert_eq!(*rhs, ValueId(150)); // base const
            }
            _ => panic!("Expected BinOp(Mul) instruction"),
        }

        // Instruction 3: BinOp(Add, tmp, digit)
        match &instructions[2] {
            JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => {
                assert_eq!(*dst, ValueId(152)); // Third allocated (final result)
                assert_eq!(*op, BinOpKind::Add);
                assert_eq!(*lhs, ValueId(151)); // tmp from previous mul
                assert_eq!(*rhs, ValueId(30)); // digit from env
            }
            _ => panic!("Expected BinOp(Add) instruction"),
        }

        assert_eq!(result_id, ValueId(152));
    }

    #[test]
    fn test_emit_number_accumulation_digit_not_found() {
        // Phase 190: Test error when digit variable not in env
        let mut cond_env = ConditionEnv::new();
        cond_env.insert("result".to_string(), ValueId(20));
        // Note: digit NOT in env

        let body_env = LoopBodyLocalEnv::new();
        let promoted: Vec<String> = vec![];
        let update_env = UpdateEnv::new(&cond_env, &body_env, &promoted);

        let carrier = test_carrier("result", 200);
        let update = UpdateExpr::BinOp {
            lhs: "result".to_string(),
            op: BinOpKind::Add,
            rhs: UpdateRhs::NumberAccumulation {
                base: 10,
                digit_var: "digit".to_string(),
            },
        };

        let mut value_counter = 160u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        let mut instructions = Vec::new();
        let result = emit_carrier_update_with_env(
            &carrier,
            &update,
            &mut alloc_value,
            &update_env,
            &mut instructions,
        );

        assert!(result.is_err());
        let err = result.unwrap_err();
        assert!(err.contains("Number accumulation digit variable 'digit' not found"));
    }
}
