use super::{MirInstruction, MirType, ValueId};
use crate::ast::{ASTNode, BinaryOperator};
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::mir::{BinaryOp, CompareOp, TypeOpKind, UnaryOp};

// Internal classification for binary operations
#[derive(Debug)]
enum BinaryOpType {
    Arithmetic(BinaryOp),
    Comparison(CompareOp),
}

impl super::MirBuilder {
    // Build a binary operation
    pub(super) fn build_binary_op(
        &mut self,
        left: ASTNode,
        operator: BinaryOperator,
        right: ASTNode,
    ) -> Result<ValueId, String> {
        // Short-circuit logical ops: lower to control-flow so RHS is evaluated conditionally
        if matches!(operator, BinaryOperator::And | BinaryOperator::Or) {
            return self.build_logical_shortcircuit(left, operator, right);
        }

        let lhs_raw = self.build_expression(left)?;
        let rhs_raw = self.build_expression(right)?;
        // Correctness-first: ensure both operands have block-local definitions
        // so they participate in PHI/materialization and avoid use-before-def across
        // complex control-flow (e.g., loop headers and nested branches).
        let lhs = self
            .ensure_slotified_for_use(lhs_raw, "@binop_lhs")
            .unwrap_or(lhs_raw);
        let rhs = self
            .ensure_slotified_for_use(rhs_raw, "@binop_rhs")
            .unwrap_or(rhs_raw);
        let dst = self.next_value_id();

        let mir_op = self.convert_binary_operator(operator)?;

        let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
            .ok()
            .as_deref()
            == Some("1");

        match mir_op {
            // Arithmetic operations
            BinaryOpType::Arithmetic(op) => {
                // Dev: Lower '+' を演算子ボックス呼び出しに置換（既定OFF）
                let in_add_op = self
                    .scope_ctx
                    .current_function
                    .as_ref()
                    .map(|f| f.signature.name.starts_with("AddOperator.apply/"))
                    .unwrap_or(false);
                if matches!(op, crate::mir::BinaryOp::Add)
                    && !in_add_op
                    && (all_call
                        || std::env::var("NYASH_BUILDER_OPERATOR_BOX_ADD_CALL")
                            .ok()
                            .as_deref()
                            == Some("1"))
                {
                    // AddOperator.apply/2(lhs, rhs)
                    let name = "AddOperator.apply/2".to_string();
                    self.emit_legacy_call(
                        Some(dst),
                        super::builder_calls::CallTarget::Global(name),
                        vec![lhs, rhs],
                    )?;
                    // Phase 196: TypeFacts SSOT - AddOperator call type annotation
                    // Phase 131-11-E: TypeFacts - classify operand types (Phase 136: use TypeFactsBox)
                    let type_facts = super::type_facts::TypeFactsBox::new(
                        &self.type_ctx.value_types,
                        &self.type_ctx.value_origin_newbox,
                    );
                    let lhs_type = type_facts.classify_operand_type(lhs);
                    let rhs_type = type_facts.classify_operand_type(rhs);

                    use super::type_facts::OperandTypeClass::*;
                    match (lhs_type, rhs_type) {
                        (String, String) => {
                            // BOTH are strings: result is string
                            self.type_ctx
                                .value_types
                                .insert(dst, MirType::Box("StringBox".to_string()));
                            self.type_ctx
                                .value_origin_newbox
                                .insert(dst, "StringBox".to_string());
                        }
                        (Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
                            // TypeFact: Integer + anything non-String = Integer
                            self.type_ctx.value_types.insert(dst, MirType::Integer);
                        }
                        (String, Integer) | (Integer, String) => {
                            // Mixed types: leave as Unknown for use-site coercion
                        }
                        (Unknown, Unknown) => {
                            // Both Unknown: cannot infer
                        }
                        (String, Unknown) | (Unknown, String) => {
                            // One side is String, other is Unknown: cannot infer safely
                        }
                    }
                } else if all_call {
                    // Lower other arithmetic ops to operator boxes under ALL flag
                    let (name, guard_prefix) = match op {
                        crate::mir::BinaryOp::Sub => ("SubOperator.apply/2", "SubOperator.apply/"),
                        crate::mir::BinaryOp::Mul => ("MulOperator.apply/2", "MulOperator.apply/"),
                        crate::mir::BinaryOp::Div => ("DivOperator.apply/2", "DivOperator.apply/"),
                        crate::mir::BinaryOp::Mod => ("ModOperator.apply/2", "ModOperator.apply/"),
                        crate::mir::BinaryOp::Shl => ("ShlOperator.apply/2", "ShlOperator.apply/"),
                        crate::mir::BinaryOp::Shr => ("ShrOperator.apply/2", "ShrOperator.apply/"),
                        crate::mir::BinaryOp::BitAnd => {
                            ("BitAndOperator.apply/2", "BitAndOperator.apply/")
                        }
                        crate::mir::BinaryOp::BitOr => {
                            ("BitOrOperator.apply/2", "BitOrOperator.apply/")
                        }
                        crate::mir::BinaryOp::BitXor => {
                            ("BitXorOperator.apply/2", "BitXorOperator.apply/")
                        }
                        _ => ("", ""),
                    };
                    if !name.is_empty() {
                        let in_guard = self
                            .scope_ctx
                            .current_function
                            .as_ref()
                            .map(|f| f.signature.name.starts_with(guard_prefix))
                            .unwrap_or(false);
                        if !in_guard {
                            self.emit_legacy_call(
                                Some(dst),
                                super::builder_calls::CallTarget::Global(name.to_string()),
                                vec![lhs, rhs],
                            )?;
                            // 型注釈: 算術はおおむね整数（Addは上で注釈済み）
                            self.type_ctx.value_types.insert(dst, MirType::Integer);
                        } else {
                            // guard中は従来のBinOp
                            if let (Some(func), Some(cur_bb)) =
                                (self.scope_ctx.current_function.as_mut(), self.current_block)
                            {
                                crate::mir::ssot::binop_lower::emit_binop_to_dst(
                                    func, cur_bb, dst, op, lhs, rhs,
                                );
                            } else {
                                self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
                            }
                            self.type_ctx.value_types.insert(dst, MirType::Integer);
                        }
                    } else {
                        // 既存の算術経路
                        if let (Some(func), Some(cur_bb)) =
                            (self.scope_ctx.current_function.as_mut(), self.current_block)
                        {
                            crate::mir::ssot::binop_lower::emit_binop_to_dst(
                                func, cur_bb, dst, op, lhs, rhs,
                            );
                        } else {
                            self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
                        }
                        // Phase 196: TypeFacts SSOT - BinOp type is determined by operands only
                        // String concatenation is handled at use-site in LLVM lowering
                        if matches!(op, crate::mir::BinaryOp::Add) {
                            // Phase 131-11-E: TypeFacts - classify operand types (Phase 136: use TypeFactsBox)
                            let type_facts = super::type_facts::TypeFactsBox::new(
                                &self.type_ctx.value_types,
                                &self.type_ctx.value_origin_newbox,
                            );
                            let lhs_type = type_facts.classify_operand_type(lhs);
                            let rhs_type = type_facts.classify_operand_type(rhs);

                            use super::type_facts::OperandTypeClass::*;
                            match (lhs_type, rhs_type) {
                                (String, String) => {
                                    // BOTH are strings: result is definitely a string
                                    self.type_ctx
                                        .value_types
                                        .insert(dst, MirType::Box("StringBox".to_string()));
                                    self.type_ctx
                                        .value_origin_newbox
                                        .insert(dst, "StringBox".to_string());
                                }
                                (Integer, Integer) | (Integer, Unknown) | (Unknown, Integer) => {
                                    // TypeFact: Integer + anything non-String = Integer
                                    // This handles `counter + 1` where counter might be Unknown
                                    self.type_ctx.value_types.insert(dst, MirType::Integer);
                                }
                                (String, Integer) | (Integer, String) => {
                                    // Mixed types: leave as Unknown for use-site coercion
                                    // LLVM backend will handle string concatenation
                                }
                                (Unknown, Unknown) => {
                                    // Both Unknown: cannot infer, leave as Unknown
                                }
                                (String, Unknown) | (Unknown, String) => {
                                    // One side is String, other is Unknown: cannot infer safely
                                    // Leave as Unknown
                                }
                            }
                        } else {
                            self.type_ctx.value_types.insert(dst, MirType::Integer);
                        }
                    }
                } else {
                    // 既存の算術経路
                    if let (Some(func), Some(cur_bb)) =
                        (self.scope_ctx.current_function.as_mut(), self.current_block)
                    {
                        crate::mir::ssot::binop_lower::emit_binop_to_dst(
                            func, cur_bb, dst, op, lhs, rhs,
                        );
                    } else {
                        self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
                    }
                    // Phase 196: TypeFacts SSOT - BinOp type is determined by operands only
                    // String concatenation is handled at use-site in LLVM lowering
                    if matches!(op, crate::mir::BinaryOp::Add) {
                        // Check if BOTH operands are known to be strings (TypeFacts)
                        let lhs_is_str = match self.type_ctx.value_types.get(&lhs) {
                            Some(MirType::String) => true,
                            Some(MirType::Box(bt)) if bt == "StringBox" => true,
                            _ => self
                                .type_ctx
                                .value_origin_newbox
                                .get(&lhs)
                                .map(|s| s == "StringBox")
                                .unwrap_or(false),
                        };
                        let rhs_is_str = match self.type_ctx.value_types.get(&rhs) {
                            Some(MirType::String) => true,
                            Some(MirType::Box(bt)) if bt == "StringBox" => true,
                            _ => self
                                .type_ctx
                                .value_origin_newbox
                                .get(&rhs)
                                .map(|s| s == "StringBox")
                                .unwrap_or(false),
                        };
                        if lhs_is_str && rhs_is_str {
                            // BOTH are strings: result is definitely a string
                            self.type_ctx
                                .value_types
                                .insert(dst, MirType::Box("StringBox".to_string()));
                            self.type_ctx
                                .value_origin_newbox
                                .insert(dst, "StringBox".to_string());
                        } else if !lhs_is_str && !rhs_is_str {
                            // NEITHER is a string: numeric addition
                            self.type_ctx.value_types.insert(dst, MirType::Integer);
                        }
                        // else: Mixed types (string + int or int + string)
                        // Leave dst type as Unknown - LLVM will handle coercion at use-site
                    } else {
                        self.type_ctx.value_types.insert(dst, MirType::Integer);
                    }
                }
            }
            // Comparison operations
            BinaryOpType::Comparison(op) => {
                // Dev: Lower 比較 を演算子ボックス呼び出しに置換（既定OFF）
                let in_cmp_op = self
                    .scope_ctx
                    .current_function
                    .as_ref()
                    .map(|f| f.signature.name.starts_with("CompareOperator.apply/"))
                    .unwrap_or(false);
                if !in_cmp_op
                    && (all_call
                        || std::env::var("NYASH_BUILDER_OPERATOR_BOX_COMPARE_CALL")
                            .ok()
                            .as_deref()
                            == Some("1"))
                {
                    // op名の文字列化
                    let opname = match op {
                        CompareOp::Eq => "Eq",
                        CompareOp::Ne => "Ne",
                        CompareOp::Lt => "Lt",
                        CompareOp::Le => "Le",
                        CompareOp::Gt => "Gt",
                        CompareOp::Ge => "Ge",
                    };
                    let op_const =
                        crate::mir::builder::emission::constant::emit_string(self, opname);
                    // そのまま値を渡す（型変換/slot化は演算子内orVMで行う）
                    let name = "CompareOperator.apply/3".to_string();
                    self.emit_legacy_call(
                        Some(dst),
                        super::builder_calls::CallTarget::Global(name),
                        vec![op_const, lhs, rhs],
                    )?;
                    self.type_ctx.value_types.insert(dst, MirType::Bool);
                } else {
                    // 既存の比較経路（安全のための型注釈/slot化含む）
                    let (lhs2_raw, rhs2_raw) = if self
                        .type_ctx
                        .value_origin_newbox
                        .get(&lhs)
                        .map(|s| s == "IntegerBox")
                        .unwrap_or(false)
                        && self
                            .type_ctx
                            .value_origin_newbox
                            .get(&rhs)
                            .map(|s| s == "IntegerBox")
                            .unwrap_or(false)
                    {
                        let li = self.next_value_id();
                        let ri = self.next_value_id();
                        self.emit_instruction(MirInstruction::TypeOp {
                            dst: li,
                            op: TypeOpKind::Cast,
                            value: lhs,
                            ty: MirType::Integer,
                        })?;
                        self.emit_instruction(MirInstruction::TypeOp {
                            dst: ri,
                            op: TypeOpKind::Cast,
                            value: rhs,
                            ty: MirType::Integer,
                        })?;
                        (li, ri)
                    } else {
                        (lhs, rhs)
                    };
                    // Finalize compare operands in current block via LocalSSA
                    let mut lhs2 = lhs2_raw;
                    let mut rhs2 = rhs2_raw;
                    crate::mir::builder::ssa::local::finalize_compare(self, &mut lhs2, &mut rhs2);
                    crate::mir::builder::emission::compare::emit_to(self, dst, op, lhs2, rhs2)?;
                }
            }
        }

        Ok(dst)
    }

    /// Lower logical && / || with proper short-circuit semantics.
    /// Result is a Bool, and RHS is only evaluated if needed.
    fn build_logical_shortcircuit(
        &mut self,
        left: ASTNode,
        operator: BinaryOperator,
        right: ASTNode,
    ) -> Result<ValueId, String> {
        let is_and = matches!(operator, BinaryOperator::And);

        // Evaluate LHS only once and pin to a slot so it can be reused safely across blocks
        let lhs_val0 = self.build_expression(left)?;
        let lhs_val = self.pin_to_slot(lhs_val0, "@sc_lhs")?;

        // Prepare blocks
        let then_block = self.next_block_id();
        let else_block = self.next_block_id();
        let merge_block = self.next_block_id();

        // Branch on LHS truthiness (runtime to_bool semantics in interpreter/LLVM)
        let mut lhs_cond = self.local_cond(lhs_val);
        crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut lhs_cond);
        crate::mir::builder::emission::branch::emit_conditional(
            self, lhs_cond, then_block, else_block,
        )?;
        // Record predecessor block for branch (for single-pred PHI materialization)
        let pre_branch_bb = self.current_block()?;

        // Snapshot variables before entering branches
        let pre_if_var_map = self.variable_ctx.variable_map.clone();

        // ---- THEN branch ----
        self.start_new_block(then_block)?;
        self.hint_scope_enter(0);
        // Reset scope to pre-if snapshot for clean deltas
        self.variable_ctx.variable_map = pre_if_var_map.clone();
        // Materialize all variables at entry via single-pred PHI (correctness-first)
        for (name, &pre_v) in pre_if_var_map.iter() {
            let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
            self.variable_ctx.variable_map.insert(name.clone(), phi_val);
        }

        // AND: then → evaluate RHS and reduce to bool
        //  OR: then → constant true
        let then_value_raw = if is_and {
            // Reduce arbitrary RHS to bool by branching on its truthiness and returning consts
            let rhs_true = self.next_block_id();
            let rhs_false = self.next_block_id();
            let rhs_join = self.next_block_id();
            let rhs_val = self.build_expression(right.clone())?;
            let mut rhs_cond = self.local_cond(rhs_val);
            crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
            crate::mir::builder::emission::branch::emit_conditional(
                self, rhs_cond, rhs_true, rhs_false,
            )?;
            // true path
            self.start_new_block(rhs_true)?;
            let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
            crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
            let rhs_true_exit = self.current_block()?;
            // false path
            self.start_new_block(rhs_false)?;
            let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
            crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
            let rhs_false_exit = self.current_block()?;
            // join rhs result into a single bool
            self.start_new_block(rhs_join)?;
            if let Some(func) = self.scope_ctx.current_function.as_mut() {
                func.update_cfg();
            }
            let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
            self.type_ctx.value_types.insert(rhs_bool, MirType::Bool);
            rhs_bool
        } else {
            let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
            t_id
        };
        let then_exit_block = self.current_block()?;
        let then_reaches_merge = !self.is_current_block_terminated();
        let then_var_map_end = self.variable_ctx.variable_map.clone();
        if then_reaches_merge {
            self.hint_scope_leave(0);
            crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
        }

        // ---- ELSE branch ----
        self.start_new_block(else_block)?;
        self.hint_scope_enter(0);
        self.variable_ctx.variable_map = pre_if_var_map.clone();
        // Materialize all variables at entry via single-pred PHI (correctness-first)
        for (name, &pre_v) in pre_if_var_map.iter() {
            let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
            self.variable_ctx.variable_map.insert(name.clone(), phi_val);
        }
        // AND: else → false
        //  OR: else → evaluate RHS and reduce to bool
        let else_value_raw = if is_and {
            let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
            f_id
        } else {
            let rhs_true = self.next_block_id();
            let rhs_false = self.next_block_id();
            let rhs_join = self.next_block_id();
            let rhs_val = self.build_expression(right)?;
            let mut rhs_cond = self.local_cond(rhs_val);
            crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut rhs_cond);
            crate::mir::builder::emission::branch::emit_conditional(
                self, rhs_cond, rhs_true, rhs_false,
            )?;
            // true path
            self.start_new_block(rhs_true)?;
            let t_id = crate::mir::builder::emission::constant::emit_bool(self, true);
            crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
            let rhs_true_exit = self.current_block()?;
            // false path
            self.start_new_block(rhs_false)?;
            let f_id = crate::mir::builder::emission::constant::emit_bool(self, false);
            crate::mir::builder::emission::branch::emit_jump(self, rhs_join)?;
            let rhs_false_exit = self.current_block()?;
            // join rhs result into a single bool
            self.start_new_block(rhs_join)?;
            if let Some(func) = self.scope_ctx.current_function.as_mut() {
                func.update_cfg();
            }
            let rhs_bool = self.insert_phi_binary(rhs_true_exit, t_id, rhs_false_exit, f_id)?;
            self.type_ctx.value_types.insert(rhs_bool, MirType::Bool);
            rhs_bool
        };
        let else_exit_block = self.current_block()?;
        let else_reaches_merge = !self.is_current_block_terminated();
        let else_var_map_end = self.variable_ctx.variable_map.clone();
        if else_reaches_merge {
            self.hint_scope_leave(0);
            crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
        }

        // ---- MERGE ----
        // Merge block: suppress entry pin copy so PHIs remain first and materialize pins explicitly
        self.suppress_next_entry_pin_copy();
        self.start_new_block(merge_block)?;
        self.push_if_merge(merge_block);

        // Result PHI (bool) — consider only reachable predecessors
        let mut inputs: Vec<(crate::mir::BasicBlockId, ValueId)> = Vec::new();
        if then_reaches_merge {
            inputs.push((then_exit_block, then_value_raw));
        }
        if else_reaches_merge {
            inputs.push((else_exit_block, else_value_raw));
        }
        let result_val = if inputs.len() >= 2 {
            if let Some(func) = self.scope_ctx.current_function.as_mut() {
                func.update_cfg();
            }
            let dst = self.insert_phi(inputs)?;
            self.type_ctx.value_types.insert(dst, MirType::Bool);
            dst
        } else if inputs.len() == 1 {
            inputs[0].1
        } else {
            // Unreachable fallthrough: synthesize false (defensive)
            crate::mir::builder::emission::constant::emit_bool(self, false)
        };

        // Merge modified vars from both branches back into current scope
        self.merge_modified_vars(
            then_block,
            else_block,
            if then_reaches_merge {
                Some(then_exit_block)
            } else {
                None
            },
            if else_reaches_merge {
                Some(else_exit_block)
            } else {
                None
            },
            &pre_if_var_map,
            &then_var_map_end,
            &Some(else_var_map_end),
            None,
        )?;

        self.pop_if_merge();
        Ok(result_val)
    }

    // Build a unary operation
    pub(super) fn build_unary_op(
        &mut self,
        operator: String,
        operand: ASTNode,
    ) -> Result<ValueId, String> {
        let operand_val = self.build_expression(operand)?;
        let all_call = std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL")
            .ok()
            .as_deref()
            == Some("1");
        if all_call {
            let (name, guard_prefix, rett) = match operator.as_str() {
                "-" => (
                    "NegOperator.apply/1",
                    "NegOperator.apply/",
                    MirType::Integer,
                ),
                "!" | "not" => ("NotOperator.apply/1", "NotOperator.apply/", MirType::Bool),
                "~" => (
                    "BitNotOperator.apply/1",
                    "BitNotOperator.apply/",
                    MirType::Integer,
                ),
                _ => ("", "", MirType::Integer),
            };
            if !name.is_empty() {
                let in_guard = self
                    .scope_ctx
                    .current_function
                    .as_ref()
                    .map(|f| f.signature.name.starts_with(guard_prefix))
                    .unwrap_or(false);
                let dst = self.next_value_id();
                if !in_guard {
                    self.emit_legacy_call(
                        Some(dst),
                        super::builder_calls::CallTarget::Global(name.to_string()),
                        vec![operand_val],
                    )?;
                    self.type_ctx.value_types.insert(dst, rett);
                    return Ok(dst);
                }
            }
        }
        // Core-13 純化: UnaryOp を直接 展開（Neg/Not/BitNot）
        if crate::config::env::mir_core13_pure() {
            match operator.as_str() {
                "-" => {
                    let zero = crate::mir::builder::emission::constant::emit_integer(self, 0);
                    let dst = self.next_value_id();
                    self.emit_instruction(MirInstruction::BinOp {
                        dst,
                        op: crate::mir::BinaryOp::Sub,
                        lhs: zero,
                        rhs: operand_val,
                    })?;
                    return Ok(dst);
                }
                "!" | "not" => {
                    let f = crate::mir::builder::emission::constant::emit_bool(self, false);
                    let dst = self.next_value_id();
                    crate::mir::builder::emission::compare::emit_to(
                        self,
                        dst,
                        crate::mir::CompareOp::Eq,
                        operand_val,
                        f,
                    )?;
                    return Ok(dst);
                }
                "~" => {
                    let all1 = crate::mir::builder::emission::constant::emit_integer(self, -1);
                    let dst = self.next_value_id();
                    self.emit_instruction(MirInstruction::BinOp {
                        dst,
                        op: crate::mir::BinaryOp::BitXor,
                        lhs: operand_val,
                        rhs: all1,
                    })?;
                    return Ok(dst);
                }
                _ => {}
            }
        }
        let dst = self.next_value_id();
        let mir_op = self.convert_unary_operator(operator)?;
        self.emit_instruction(MirInstruction::UnaryOp {
            dst,
            op: mir_op,
            operand: operand_val,
        })?;
        Ok(dst)
    }

    // Convert AST binary operator to MIR enum or compare
    fn convert_binary_operator(&self, op: BinaryOperator) -> Result<BinaryOpType, String> {
        match op {
            BinaryOperator::Add => Ok(BinaryOpType::Arithmetic(BinaryOp::Add)),
            BinaryOperator::Subtract => Ok(BinaryOpType::Arithmetic(BinaryOp::Sub)),
            BinaryOperator::Multiply => Ok(BinaryOpType::Arithmetic(BinaryOp::Mul)),
            BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)),
            BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)),
            BinaryOperator::Shl => Ok(BinaryOpType::Arithmetic(BinaryOp::Shl)),
            BinaryOperator::Shr => Ok(BinaryOpType::Arithmetic(BinaryOp::Shr)),
            BinaryOperator::BitAnd => Ok(BinaryOpType::Arithmetic(BinaryOp::BitAnd)),
            BinaryOperator::BitOr => Ok(BinaryOpType::Arithmetic(BinaryOp::BitOr)),
            BinaryOperator::BitXor => Ok(BinaryOpType::Arithmetic(BinaryOp::BitXor)),
            BinaryOperator::Equal => Ok(BinaryOpType::Comparison(CompareOp::Eq)),
            BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)),
            BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)),
            BinaryOperator::LessEqual => Ok(BinaryOpType::Comparison(CompareOp::Le)),
            BinaryOperator::Greater => Ok(BinaryOpType::Comparison(CompareOp::Gt)),
            BinaryOperator::GreaterEqual => Ok(BinaryOpType::Comparison(CompareOp::Ge)),
            BinaryOperator::And => Ok(BinaryOpType::Arithmetic(BinaryOp::And)),
            BinaryOperator::Or => Ok(BinaryOpType::Arithmetic(BinaryOp::Or)),
        }
    }

    // Convert AST unary operator to MIR operator
    fn convert_unary_operator(&self, op: String) -> Result<UnaryOp, String> {
        match op.as_str() {
            "-" => Ok(UnaryOp::Neg),
            "!" | "not" => Ok(UnaryOp::Not),
            "~" => Ok(UnaryOp::BitNot),
            _ => Err(format!("Unsupported unary operator: {}", op)),
        }
    }
}
