// ===============================================================================
// QUANTALANG CODE GENERATOR - X86-64 BACKEND
// ===============================================================================
// Copyright (c) 2022-2026 Zain Dana Harper. MIT License.
// ===============================================================================

//! x86-64 native code generation backend.
//!
//! Generates native machine code for x86-64 processors.
//!
//! ## Output Modes
//!
//! - **Assembly**: Generates GNU-syntax assembly text (default)
//! - **MachineCode**: Generates raw machine code using the instruction encoder
//!
//! ## Example
//!
//! ```rust,ignore
//! // Assembly output (default)
//! let mut backend = X86_64Backend::new();
//! let output = backend.generate(&mir)?;
//!
//! // Machine code output
//! let mut backend = X86_64Backend::new().with_machine_code();
//! let output = backend.generate(&mir)?;
//! ```

// NOTE: `.unwrap()` calls in this backend are intentional assertions on
// type-checked AST invariants. Failures indicate compiler bugs in earlier
// phases, not user input errors. See codegen/mod.rs for the full unwrap policy.

use std::collections::HashMap;
use std::fmt::Write;

use super::x86_64_enc::{Cond, Mem, Reg64, Reg8, X86_64Encoder};
use super::{Backend, CodegenResult, Target};
use crate::codegen::debug::DwarfGenerator;
use crate::codegen::ir::*;
use crate::codegen::{GeneratedCode, OutputFormat};

/// Output mode for the x86-64 backend.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86_64OutputMode {
    /// Generate assembly text.
    Assembly,
    /// Generate raw machine code.
    MachineCode,
}

/// x86-64 backend for code generation.
pub struct X86_64Backend {
    /// Output buffer (assembly).
    output: String,
    /// Label counter.
    label_counter: u32,
    /// Output mode.
    mode: X86_64OutputMode,
    /// Machine code encoder (when mode is MachineCode).
    encoder: Option<X86_64Encoder>,
    /// Generate debug information.
    generate_debug: bool,
    /// DWARF generator for debug info.
    dwarf: Option<DwarfGenerator>,
    /// Local to stack offset mapping.
    local_offsets: HashMap<LocalId, i32>,
    /// Function address ranges for debug info.
    func_ranges: Vec<(String, u64, u64)>,
}

impl X86_64Backend {
    /// Create a new x86-64 backend.
    pub fn new() -> Self {
        Self {
            output: String::new(),
            label_counter: 0,
            mode: X86_64OutputMode::Assembly,
            encoder: None,
            generate_debug: false,
            dwarf: None,
            local_offsets: HashMap::new(),
            func_ranges: Vec::new(),
        }
    }

    /// Enable machine code output mode.
    pub fn with_machine_code(mut self) -> Self {
        self.mode = X86_64OutputMode::MachineCode;
        self.encoder = Some(X86_64Encoder::new());
        self
    }

    /// Enable debug information generation.
    pub fn with_debug_info(mut self) -> Self {
        self.generate_debug = true;
        self.dwarf = Some(DwarfGenerator::new());
        self
    }

    /// Generate a fresh label.
    fn fresh_label(&mut self, prefix: &str) -> String {
        let label = format!(".L{}_{}", prefix, self.label_counter);
        self.label_counter += 1;
        label
    }

    /// Get encoder reference (panics if not in machine code mode).
    fn enc(&mut self) -> &mut X86_64Encoder {
        self.encoder
            .as_mut()
            .expect("Machine code mode not enabled")
    }

    fn generate_module(&mut self, module: &MirModule) -> CodegenResult<()> {
        // Assembly header
        self.output.push_str("# Generated by QuantaLang Compiler\n");
        self.output.push_str("# Target: x86-64\n\n");
        self.output.push_str(".intel_syntax noprefix\n\n");

        // Data section
        self.output.push_str(".section .data\n");
        self.generate_data_section(module)?;

        // Read-only data
        self.output.push_str("\n.section .rodata\n");
        self.generate_rodata_section(module)?;

        // Text section (code)
        self.output.push_str("\n.section .text\n");
        for func in &module.functions {
            if !func.is_declaration() {
                self.generate_function(func)?;
            }
        }

        Ok(())
    }

    fn generate_data_section(&mut self, module: &MirModule) -> CodegenResult<()> {
        for global in &module.globals {
            if global.is_mut {
                write!(self.output, ".globl {}\n", global.name).unwrap();
                write!(self.output, "{}:\n", global.name).unwrap();
                if let Some(init) = &global.init {
                    self.emit_const_data(init, &global.ty)?;
                } else {
                    let size = self.type_size(&global.ty);
                    write!(self.output, "    .zero {}\n", size).unwrap();
                }
            }
        }
        Ok(())
    }

    fn generate_rodata_section(&mut self, module: &MirModule) -> CodegenResult<()> {
        // String literals
        for (i, s) in module.strings.iter().enumerate() {
            write!(self.output, "__str{}:\n", i).unwrap();
            write!(self.output, "    .asciz \"{}\"\n", self.escape_string(s)).unwrap();
        }

        // Constant globals
        for global in &module.globals {
            if !global.is_mut {
                write!(self.output, ".globl {}\n", global.name).unwrap();
                write!(self.output, "{}:\n", global.name).unwrap();
                if let Some(init) = &global.init {
                    self.emit_const_data(init, &global.ty)?;
                } else {
                    let size = self.type_size(&global.ty);
                    write!(self.output, "    .zero {}\n", size).unwrap();
                }
            }
        }
        Ok(())
    }

    fn generate_function(&mut self, func: &MirFunction) -> CodegenResult<()> {
        // Function prologue
        if func.is_public {
            write!(self.output, ".globl {}\n", func.name).unwrap();
        }
        write!(self.output, "{}:\n", func.name).unwrap();

        // Standard prologue
        self.output.push_str("    push rbp\n");
        self.output.push_str("    mov rbp, rsp\n");

        // Allocate stack space for locals
        let stack_size = self.calculate_stack_size(func);
        if stack_size > 0 {
            write!(self.output, "    sub rsp, {}\n", stack_size).unwrap();
        }

        // Save callee-saved registers if needed
        // TODO: proper register allocation

        // Generate blocks
        if let Some(blocks) = &func.blocks {
            for block in blocks {
                self.generate_block(block, func)?;
            }
        }

        self.output.push('\n');
        Ok(())
    }

    fn generate_block(&mut self, block: &MirBlock, func: &MirFunction) -> CodegenResult<()> {
        // Block label
        let label = block
            .label
            .as_ref()
            .map(|l| l.to_string())
            .unwrap_or_else(|| format!(".Lbb{}_{}", func.name, block.id.0));
        write!(self.output, "{}:\n", label).unwrap();

        // Generate statements
        for stmt in &block.stmts {
            self.generate_statement(stmt, func)?;
        }

        // Generate terminator
        if let Some(term) = &block.terminator {
            self.generate_terminator(term, func)?;
        }

        Ok(())
    }

    fn generate_statement(&mut self, stmt: &MirStmt, func: &MirFunction) -> CodegenResult<()> {
        match &stmt.kind {
            MirStmtKind::Assign { dest, value } => {
                self.generate_assign(*dest, value, func)?;
            }
            MirStmtKind::DerefAssign { ptr, .. } => {
                let offset = self.local_stack_offset(*ptr, func);
                self.output.push_str(&format!(
                    "    ; deref store through local at rbp-{}\n",
                    offset
                ));
                self.output
                    .push_str(&format!("    mov rbx, [rbp-{}]\n", offset));
                self.output.push_str("    mov qword [rbx], rax\n");
            }
            MirStmtKind::FieldDerefAssign {
                ptr, field_name, ..
            } => {
                let offset = self.local_stack_offset(*ptr, func);
                self.output.push_str(&format!(
                    "    ; field deref store .{} through local at rbp-{}\n",
                    field_name, offset
                ));
                self.output
                    .push_str(&format!("    mov rbx, [rbp-{}]\n", offset));
                self.output.push_str("    mov qword [rbx], rax\n");
            }
            MirStmtKind::FieldAssign {
                base, field_name, ..
            } => {
                let offset = self.local_stack_offset(*base, func);
                self.output.push_str(&format!(
                    "    ; field store .{} on local at rbp-{}\n",
                    field_name, offset
                ));
                self.output.push_str("    mov qword [rbx], rax\n");
            }
            MirStmtKind::StorageLive(_) | MirStmtKind::StorageDead(_) => {
                // No-op
            }
            MirStmtKind::Nop => {
                self.output.push_str("    nop\n");
            }
        }
        Ok(())
    }

    fn generate_assign(
        &mut self,
        dest: LocalId,
        value: &MirRValue,
        func: &MirFunction,
    ) -> CodegenResult<()> {
        match value {
            MirRValue::Use(val) => {
                self.load_value_to_rax(val, func)?;
                self.store_rax_to_local(dest, func)?;
            }
            MirRValue::BinaryOp { op, left, right } => {
                // Load left into rax
                self.load_value_to_rax(left, func)?;
                self.output.push_str("    push rax\n");
                // Load right into rcx
                self.load_value_to_rax(right, func)?;
                self.output.push_str("    mov rcx, rax\n");
                self.output.push_str("    pop rax\n");
                // Perform operation
                self.emit_binary_op(*op)?;
                // Store result
                self.store_rax_to_local(dest, func)?;
            }
            MirRValue::UnaryOp { op, operand } => {
                self.load_value_to_rax(operand, func)?;
                match op {
                    UnaryOp::Neg => self.output.push_str("    neg rax\n"),
                    UnaryOp::Not => self.output.push_str("    not rax\n"),
                }
                self.store_rax_to_local(dest, func)?;
            }
            _ => {
                // TODO: implement other rvalues
                write!(self.output, "    # TODO: rvalue {:?}\n", value).unwrap();
            }
        }
        Ok(())
    }

    fn generate_terminator(
        &mut self,
        term: &MirTerminator,
        func: &MirFunction,
    ) -> CodegenResult<()> {
        match term {
            MirTerminator::Goto(target) => {
                write!(self.output, "    jmp .Lbb{}_{}\n", func.name, target.0).unwrap();
            }
            MirTerminator::If {
                cond,
                then_block,
                else_block,
            } => {
                self.load_value_to_rax(cond, func)?;
                self.output.push_str("    test rax, rax\n");
                write!(self.output, "    jnz .Lbb{}_{}\n", func.name, then_block.0).unwrap();
                write!(self.output, "    jmp .Lbb{}_{}\n", func.name, else_block.0).unwrap();
            }
            MirTerminator::Return(value) => {
                if let Some(val) = value {
                    self.load_value_to_rax(val, func)?;
                }
                // Epilogue
                self.output.push_str("    mov rsp, rbp\n");
                self.output.push_str("    pop rbp\n");
                self.output.push_str("    ret\n");
            }
            MirTerminator::Call {
                func: callee,
                args,
                dest,
                target,
                ..
            } => {
                // Pass arguments in registers (System V ABI)
                let arg_regs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
                for (i, arg) in args.iter().enumerate() {
                    if i < arg_regs.len() {
                        self.load_value_to_rax(arg, func)?;
                        write!(self.output, "    mov {}, rax\n", arg_regs[i]).unwrap();
                    } else {
                        // Stack arguments
                        self.load_value_to_rax(arg, func)?;
                        self.output.push_str("    push rax\n");
                    }
                }
                // Call
                let callee_name = match callee {
                    MirValue::Function(name) => name.to_string(),
                    _ => "unknown".to_string(),
                };
                write!(self.output, "    call {}\n", callee_name).unwrap();
                // Store result
                if let Some(d) = dest {
                    self.store_rax_to_local(*d, func)?;
                }
                // Jump to continue block
                if let Some(target_block) = target {
                    write!(
                        self.output,
                        "    jmp .Lbb{}_{}\n",
                        func.name, target_block.0
                    )
                    .unwrap();
                }
            }
            MirTerminator::Unreachable => {
                self.output.push_str("    ud2\n");
            }
            MirTerminator::Abort => {
                self.output.push_str("    call abort\n");
            }
            _ => {
                write!(self.output, "    # TODO: terminator {:?}\n", term).unwrap();
            }
        }
        Ok(())
    }

    fn load_value_to_rax(&mut self, value: &MirValue, func: &MirFunction) -> CodegenResult<()> {
        match value {
            MirValue::Local(id) => {
                let offset = self.local_stack_offset(*id, func);
                write!(self.output, "    mov rax, [rbp - {}]\n", offset).unwrap();
            }
            MirValue::Const(c) => match c {
                MirConst::Int(v, _) => {
                    write!(self.output, "    mov rax, {}\n", v).unwrap();
                }
                MirConst::Uint(v, _) => {
                    write!(self.output, "    mov rax, {}\n", v).unwrap();
                }
                MirConst::Bool(b) => {
                    write!(self.output, "    mov rax, {}\n", if *b { 1 } else { 0 }).unwrap();
                }
                _ => {
                    self.output.push_str("    xor rax, rax\n");
                }
            },
            MirValue::Global(name) => {
                write!(self.output, "    lea rax, [rip + {}]\n", name).unwrap();
            }
            MirValue::Function(name) => {
                write!(self.output, "    lea rax, [rip + {}]\n", name).unwrap();
            }
        }
        Ok(())
    }

    fn store_rax_to_local(&mut self, id: LocalId, func: &MirFunction) -> CodegenResult<()> {
        let offset = self.local_stack_offset(id, func);
        write!(self.output, "    mov [rbp - {}], rax\n", offset).unwrap();
        Ok(())
    }

    fn emit_binary_op(&mut self, op: BinOp) -> CodegenResult<()> {
        match op {
            BinOp::Add | BinOp::AddChecked | BinOp::AddWrapping | BinOp::AddSaturating => {
                self.output.push_str("    add rax, rcx\n");
            }
            BinOp::Sub | BinOp::SubChecked | BinOp::SubWrapping | BinOp::SubSaturating => {
                self.output.push_str("    sub rax, rcx\n");
            }
            BinOp::Mul | BinOp::MulChecked | BinOp::MulWrapping => {
                self.output.push_str("    imul rax, rcx\n");
            }
            BinOp::Div => {
                self.output.push_str("    cqo\n");
                self.output.push_str("    idiv rcx\n");
            }
            BinOp::Rem => {
                self.output.push_str("    cqo\n");
                self.output.push_str("    idiv rcx\n");
                self.output.push_str("    mov rax, rdx\n");
            }
            BinOp::BitAnd => {
                self.output.push_str("    and rax, rcx\n");
            }
            BinOp::BitOr => {
                self.output.push_str("    or rax, rcx\n");
            }
            BinOp::BitXor => {
                self.output.push_str("    xor rax, rcx\n");
            }
            BinOp::Shl => {
                self.output.push_str("    shl rax, cl\n");
            }
            BinOp::Shr => {
                self.output.push_str("    sar rax, cl\n");
            }
            BinOp::Eq => {
                self.output.push_str("    cmp rax, rcx\n");
                self.output.push_str("    sete al\n");
                self.output.push_str("    movzx rax, al\n");
            }
            BinOp::Ne => {
                self.output.push_str("    cmp rax, rcx\n");
                self.output.push_str("    setne al\n");
                self.output.push_str("    movzx rax, al\n");
            }
            BinOp::Lt => {
                self.output.push_str("    cmp rax, rcx\n");
                self.output.push_str("    setl al\n");
                self.output.push_str("    movzx rax, al\n");
            }
            BinOp::Le => {
                self.output.push_str("    cmp rax, rcx\n");
                self.output.push_str("    setle al\n");
                self.output.push_str("    movzx rax, al\n");
            }
            BinOp::Gt => {
                self.output.push_str("    cmp rax, rcx\n");
                self.output.push_str("    setg al\n");
                self.output.push_str("    movzx rax, al\n");
            }
            BinOp::Ge => {
                self.output.push_str("    cmp rax, rcx\n");
                self.output.push_str("    setge al\n");
                self.output.push_str("    movzx rax, al\n");
            }
            BinOp::Pow => {
                // Integer power: base (rax) ** exponent (rcx)
                // Using repeated squaring algorithm
                let label = self.label_counter;
                self.label_counter += 1;
                // r8 = result (starts at 1), rax = base, rcx = exponent
                self.output.push_str("    mov r8, 1\n"); // result = 1
                write!(self.output, ".Lpow_loop_{}:\n", label).unwrap();
                self.output.push_str("    test rcx, rcx\n"); // while exp != 0
                write!(self.output, "    jz .Lpow_done_{}\n", label).unwrap();
                self.output.push_str("    test rcx, 1\n"); // if exp & 1
                write!(self.output, "    jz .Lpow_skip_{}\n", label).unwrap();
                self.output.push_str("    imul r8, rax\n"); // result *= base
                write!(self.output, ".Lpow_skip_{}:\n", label).unwrap();
                self.output.push_str("    imul rax, rax\n"); // base *= base
                self.output.push_str("    shr rcx, 1\n"); // exp >>= 1
                write!(self.output, "    jmp .Lpow_loop_{}\n", label).unwrap();
                write!(self.output, ".Lpow_done_{}:\n", label).unwrap();
                self.output.push_str("    mov rax, r8\n"); // return result
            }
        }
        Ok(())
    }

    fn emit_const_data(&mut self, c: &MirConst, ty: &MirType) -> CodegenResult<()> {
        match c {
            MirConst::Int(v, _) => {
                let size = self.type_size(ty);
                match size {
                    1 => write!(self.output, "    .byte {}\n", v).unwrap(),
                    2 => write!(self.output, "    .word {}\n", v).unwrap(),
                    4 => write!(self.output, "    .long {}\n", v).unwrap(),
                    8 => write!(self.output, "    .quad {}\n", v).unwrap(),
                    _ => write!(self.output, "    .quad {}\n", v).unwrap(),
                }
            }
            MirConst::Uint(v, _) => {
                let size = self.type_size(ty);
                match size {
                    1 => write!(self.output, "    .byte {}\n", v).unwrap(),
                    2 => write!(self.output, "    .word {}\n", v).unwrap(),
                    4 => write!(self.output, "    .long {}\n", v).unwrap(),
                    8 => write!(self.output, "    .quad {}\n", v).unwrap(),
                    _ => write!(self.output, "    .quad {}\n", v).unwrap(),
                }
            }
            MirConst::Float(v, ty) => match ty {
                MirType::Float(FloatSize::F32) => {
                    write!(self.output, "    .float {}\n", v).unwrap();
                }
                _ => {
                    write!(self.output, "    .double {}\n", v).unwrap();
                }
            },
            MirConst::Bool(b) => {
                write!(self.output, "    .byte {}\n", if *b { 1 } else { 0 }).unwrap();
            }
            _ => {
                let size = self.type_size(ty);
                write!(self.output, "    .zero {}\n", size).unwrap();
            }
        }
        Ok(())
    }

    fn calculate_stack_size(&self, func: &MirFunction) -> u32 {
        let mut size = 0u32;
        for local in &func.locals {
            if !local.is_param {
                size += self.type_size(&local.ty) as u32;
            }
        }
        // Align to 16 bytes
        (size + 15) & !15
    }

    fn local_stack_offset(&self, id: LocalId, func: &MirFunction) -> u32 {
        let mut offset = 8; // Start after saved rbp
        for local in &func.locals {
            if local.id == id {
                return offset;
            }
            if !local.is_param {
                offset += self.type_size(&local.ty) as u32;
            }
        }
        offset
    }

    fn type_size(&self, ty: &MirType) -> usize {
        match ty {
            MirType::Void => 0,
            MirType::Bool => 1,
            MirType::Int(size, _) => match size {
                IntSize::I8 => 1,
                IntSize::I16 => 2,
                IntSize::I32 => 4,
                IntSize::I64 | IntSize::ISize => 8,
                IntSize::I128 => 16,
            },
            MirType::Float(size) => match size {
                FloatSize::F32 => 4,
                FloatSize::F64 => 8,
            },
            MirType::Ptr(_) => 8,
            MirType::Array(elem, len) => self.type_size(elem) * (*len as usize),
            MirType::Slice(_) => 16, // ptr + len
            MirType::Struct(_) => 8, // TODO: lookup actual size
            MirType::FnPtr(_) => 8,
            MirType::Never => 0,
            MirType::Vector(elem, lanes) => self.type_size(elem) * (*lanes as usize),
            // Opaque GPU types — treat as pointer-sized handles
            MirType::Texture2D(_) | MirType::Sampler | MirType::SampledImage(_) => 8,
            MirType::TraitObject(_) => 16, // fat pointer: data ptr + vtable ptr
            MirType::Vec(_) => 8,          // QuantaVecHandle is a pointer
            MirType::Tuple(elems) => elems.iter().map(|e| self.type_size(e)).sum(),
            MirType::Map(_, _) => 8, // QuantaMapHandle is a pointer
        }
    }

    fn escape_string(&self, s: &str) -> String {
        let mut result = String::new();
        for c in s.chars() {
            match c {
                '\n' => result.push_str("\\n"),
                '\r' => result.push_str("\\r"),
                '\t' => result.push_str("\\t"),
                '\\' => result.push_str("\\\\"),
                '"' => result.push_str("\\\""),
                c if c.is_ascii_control() => {
                    result.push_str(&format!("\\{:03o}", c as u8));
                }
                c => result.push(c),
            }
        }
        result
    }
}

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

impl Backend for X86_64Backend {
    fn generate(&mut self, mir: &MirModule) -> CodegenResult<GeneratedCode> {
        self.output.clear();
        self.label_counter = 0;
        self.local_offsets.clear();
        self.func_ranges.clear();

        match self.mode {
            X86_64OutputMode::Assembly => {
                self.generate_module(mir)?;
                Ok(GeneratedCode::new(
                    OutputFormat::Assembly,
                    self.output.as_bytes().to_vec(),
                ))
            }
            X86_64OutputMode::MachineCode => {
                self.generate_machine_code(mir)?;
                let code = self.encoder.take().unwrap().finish();
                Ok(GeneratedCode::new(OutputFormat::Object, code))
            }
        }
    }

    fn target(&self) -> Target {
        Target::X86_64
    }
}

// =============================================================================
// Machine Code Generation
// =============================================================================

impl X86_64Backend {
    /// Generate machine code from MIR.
    fn generate_machine_code(&mut self, module: &MirModule) -> CodegenResult<()> {
        // Initialize encoder
        self.encoder = Some(X86_64Encoder::new());

        // Generate functions
        for func in &module.functions {
            if !func.is_declaration() {
                self.generate_function_machine_code(func)?;
            }
        }

        Ok(())
    }

    /// Generate machine code for a function.
    fn generate_function_machine_code(&mut self, func: &MirFunction) -> CodegenResult<()> {
        let start_pos = self.enc().position() as u64;

        // Build local offset map
        self.build_local_offsets(func);

        // Function prologue
        self.enc().push(Reg64::RBP);
        self.enc().mov_rr(Reg64::RBP, Reg64::RSP);

        // Allocate stack space
        let stack_size = self.calculate_stack_size(func);
        if stack_size > 0 {
            self.enc().sub_ri32(Reg64::RSP, stack_size as i32);
        }

        // Move parameters from registers to stack
        let param_regs = [
            Reg64::RDI,
            Reg64::RSI,
            Reg64::RDX,
            Reg64::RCX,
            Reg64::R8,
            Reg64::R9,
        ];
        let mut param_idx = 0;
        for local in &func.locals {
            if local.is_param && param_idx < param_regs.len() {
                let offset = self.local_offsets.get(&local.id).copied().unwrap_or(8);
                let mem = Mem::base_disp(Reg64::RBP, -offset);
                self.enc().mov_mr(&mem, param_regs[param_idx]);
                param_idx += 1;
            }
        }

        // Create label map for blocks
        let mut block_labels: HashMap<BlockId, u32> = HashMap::new();
        if let Some(blocks) = &func.blocks {
            for block in blocks {
                let label = self.enc().new_label();
                block_labels.insert(block.id, label);
            }
        }

        // Generate blocks
        if let Some(blocks) = &func.blocks {
            for block in blocks {
                let label = block_labels[&block.id];
                self.enc().define_label(label);
                self.generate_block_machine_code(block, func, &block_labels)?;
            }
        }

        let end_pos = self.enc().position() as u64;
        self.func_ranges
            .push((func.name.to_string(), start_pos, end_pos - start_pos));

        Ok(())
    }

    /// Build local variable to stack offset mapping.
    fn build_local_offsets(&mut self, func: &MirFunction) {
        self.local_offsets.clear();
        let mut offset = 8i32; // Start after saved RBP

        for local in &func.locals {
            let size = self.type_size(&local.ty) as i32;
            offset += size;
            // Align to size (up to 8)
            let align = size.min(8);
            offset = (offset + align - 1) & !(align - 1);
            self.local_offsets.insert(local.id, offset);
        }
    }

    /// Generate machine code for a basic block.
    fn generate_block_machine_code(
        &mut self,
        block: &MirBlock,
        func: &MirFunction,
        block_labels: &HashMap<BlockId, u32>,
    ) -> CodegenResult<()> {
        // Generate statements
        for stmt in &block.stmts {
            self.generate_stmt_machine_code(stmt, func)?;
        }

        // Generate terminator
        if let Some(term) = &block.terminator {
            self.generate_terminator_machine_code(term, func, block_labels)?;
        }

        Ok(())
    }

    /// Generate machine code for a statement.
    fn generate_stmt_machine_code(
        &mut self,
        stmt: &MirStmt,
        func: &MirFunction,
    ) -> CodegenResult<()> {
        match &stmt.kind {
            MirStmtKind::Assign { dest, value } => {
                self.generate_assign_machine_code(*dest, value, func)?;
            }
            MirStmtKind::DerefAssign { .. }
            | MirStmtKind::FieldDerefAssign { .. }
            | MirStmtKind::FieldAssign { .. } => {
                // Pointer/field store in machine code: emit nop placeholder
                // Full implementation requires register allocator
                self.enc().nop();
            }
            MirStmtKind::StorageLive(_) | MirStmtKind::StorageDead(_) => {
                // No-op
            }
            MirStmtKind::Nop => {
                self.enc().nop();
            }
        }
        Ok(())
    }

    /// Generate machine code for an assignment.
    fn generate_assign_machine_code(
        &mut self,
        dest: LocalId,
        value: &MirRValue,
        func: &MirFunction,
    ) -> CodegenResult<()> {
        match value {
            MirRValue::Use(val) => {
                self.load_value_to_reg(val, Reg64::RAX, func)?;
                self.store_reg_to_local(Reg64::RAX, dest)?;
            }
            MirRValue::BinaryOp { op, left, right } => {
                // Load left into RAX
                self.load_value_to_reg(left, Reg64::RAX, func)?;
                self.enc().push(Reg64::RAX);

                // Load right into RCX
                self.load_value_to_reg(right, Reg64::RCX, func)?;

                // Pop left back to RAX
                self.enc().pop(Reg64::RAX);

                // Perform operation
                self.emit_binary_op_machine_code(*op)?;

                // Store result
                self.store_reg_to_local(Reg64::RAX, dest)?;
            }
            MirRValue::UnaryOp { op, operand } => {
                self.load_value_to_reg(operand, Reg64::RAX, func)?;
                match op {
                    UnaryOp::Neg => self.enc().neg(Reg64::RAX),
                    UnaryOp::Not => self.enc().not(Reg64::RAX),
                }
                self.store_reg_to_local(Reg64::RAX, dest)?;
            }
            MirRValue::Ref { place, .. } => {
                // Load address of place (only for simple local places)
                if place.projections.is_empty() {
                    let offset = self.local_offsets.get(&place.local).copied().unwrap_or(8);
                    let mem = Mem::base_disp(Reg64::RBP, -offset);
                    self.enc().lea(Reg64::RAX, &mem);
                    self.store_reg_to_local(Reg64::RAX, dest)?;
                }
            }
            MirRValue::Cast { value, ty: _, .. } => {
                self.load_value_to_reg(value, Reg64::RAX, func)?;
                // Most casts are no-ops at machine code level
                // Handle sign/zero extension as needed
                self.store_reg_to_local(Reg64::RAX, dest)?;
            }
            _ => {
                // Default: just store zero
                self.enc().xor_rr(Reg64::RAX, Reg64::RAX);
                self.store_reg_to_local(Reg64::RAX, dest)?;
            }
        }
        Ok(())
    }

    /// Load a value into a register.
    fn load_value_to_reg(
        &mut self,
        value: &MirValue,
        reg: Reg64,
        _func: &MirFunction,
    ) -> CodegenResult<()> {
        match value {
            MirValue::Local(id) => {
                let offset = self.local_offsets.get(id).copied().unwrap_or(8);
                let mem = Mem::base_disp(Reg64::RBP, -offset);
                self.enc().mov_rm(reg, &mem);
            }
            MirValue::Const(c) => {
                match c {
                    MirConst::Int(v, _) => {
                        let v = *v as i64;
                        if v >= i32::MIN as i64 && v <= i32::MAX as i64 {
                            self.enc().mov_ri32(reg, v as i32);
                        } else {
                            self.enc().mov_ri64(reg, v);
                        }
                    }
                    MirConst::Uint(v, _) => {
                        let v = *v as i64;
                        if v >= i32::MIN as i64 && v <= i32::MAX as i64 {
                            self.enc().mov_ri32(reg, v as i32);
                        } else {
                            self.enc().mov_ri64(reg, v);
                        }
                    }
                    MirConst::Bool(b) => {
                        if *b {
                            self.enc().mov_ri32(reg, 1);
                        } else {
                            self.enc().xor_rr(reg, reg);
                        }
                    }
                    MirConst::Float(v, ty) => {
                        // Load float via memory or immediate
                        // For now, load the bit pattern as integer
                        match ty {
                            MirType::Float(FloatSize::F32) => {
                                let bits = (*v as f32).to_bits();
                                self.enc().mov_ri32(reg, bits as i32);
                            }
                            _ => {
                                let bits = v.to_bits();
                                self.enc().mov_ri64(reg, bits as i64);
                            }
                        }
                    }
                    _ => {
                        self.enc().xor_rr(reg, reg);
                    }
                }
            }
            MirValue::Global(_name) => {
                // RIP-relative addressing for globals
                let mem = Mem::rip_relative(0);
                self.enc().lea(reg, &mem);
                // Note: actual relocation would be needed here
            }
            MirValue::Function(_name) => {
                // Load function address
                let mem = Mem::rip_relative(0);
                self.enc().lea(reg, &mem);
            }
        }
        Ok(())
    }

    /// Store a register to a local variable.
    fn store_reg_to_local(&mut self, reg: Reg64, id: LocalId) -> CodegenResult<()> {
        let offset = self.local_offsets.get(&id).copied().unwrap_or(8);
        let mem = Mem::base_disp(Reg64::RBP, -offset);
        self.enc().mov_mr(&mem, reg);
        Ok(())
    }

    /// Emit machine code for a binary operation.
    fn emit_binary_op_machine_code(&mut self, op: BinOp) -> CodegenResult<()> {
        match op {
            BinOp::Add | BinOp::AddChecked | BinOp::AddWrapping | BinOp::AddSaturating => {
                self.enc().add_rr(Reg64::RAX, Reg64::RCX);
            }
            BinOp::Sub | BinOp::SubChecked | BinOp::SubWrapping | BinOp::SubSaturating => {
                self.enc().sub_rr(Reg64::RAX, Reg64::RCX);
            }
            BinOp::Mul | BinOp::MulChecked | BinOp::MulWrapping => {
                self.enc().imul_rr(Reg64::RAX, Reg64::RCX);
            }
            BinOp::Div => {
                self.enc().cqo();
                self.enc().idiv(Reg64::RCX);
            }
            BinOp::Rem => {
                self.enc().cqo();
                self.enc().idiv(Reg64::RCX);
                self.enc().mov_rr(Reg64::RAX, Reg64::RDX);
            }
            BinOp::BitAnd => {
                self.enc().and_rr(Reg64::RAX, Reg64::RCX);
            }
            BinOp::BitOr => {
                self.enc().or_rr(Reg64::RAX, Reg64::RCX);
            }
            BinOp::BitXor => {
                self.enc().xor_rr(Reg64::RAX, Reg64::RCX);
            }
            BinOp::Shl => {
                self.enc().shl_cl(Reg64::RAX);
            }
            BinOp::Shr => {
                self.enc().sar_cl(Reg64::RAX);
            }
            BinOp::Eq => {
                self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
                self.enc().setcc(Cond::E, Reg8::AL);
                self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
            }
            BinOp::Ne => {
                self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
                self.enc().setcc(Cond::NE, Reg8::AL);
                self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
            }
            BinOp::Lt => {
                self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
                self.enc().setcc(Cond::L, Reg8::AL);
                self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
            }
            BinOp::Le => {
                self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
                self.enc().setcc(Cond::LE, Reg8::AL);
                self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
            }
            BinOp::Gt => {
                self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
                self.enc().setcc(Cond::G, Reg8::AL);
                self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
            }
            BinOp::Ge => {
                self.enc().cmp_rr(Reg64::RAX, Reg64::RCX);
                self.enc().setcc(Cond::GE, Reg8::AL);
                self.enc().movzx_r64_r8(Reg64::RAX, Reg8::AL);
            }
            BinOp::Pow => {
                // Integer power using repeated squaring algorithm
                // r8 = result (1), rax = base, rcx = exponent
                let loop_label = self.enc().create_label();
                let done_label = self.enc().create_label();
                let skip_label = self.enc().create_label();

                self.enc().mov_ri(Reg64::R8, 1); // result = 1
                self.enc().bind_label(loop_label);
                self.enc().test_rr(Reg64::RCX, Reg64::RCX);
                self.enc().jcc_label(Cond::E, done_label); // if exp == 0, done
                self.enc().test_ri(Reg64::RCX, 1);
                self.enc().jcc_label(Cond::E, skip_label); // if !(exp & 1), skip
                self.enc().imul_rr(Reg64::R8, Reg64::RAX); // result *= base
                self.enc().bind_label(skip_label);
                self.enc().imul_rr(Reg64::RAX, Reg64::RAX); // base *= base
                self.enc().shr_ri(Reg64::RCX, 1); // exp >>= 1
                self.enc().jmp_label(loop_label);
                self.enc().bind_label(done_label);
                self.enc().mov_rr(Reg64::RAX, Reg64::R8); // return result
            }
        }
        Ok(())
    }

    /// Generate machine code for a terminator.
    fn generate_terminator_machine_code(
        &mut self,
        term: &MirTerminator,
        func: &MirFunction,
        block_labels: &HashMap<BlockId, u32>,
    ) -> CodegenResult<()> {
        match term {
            MirTerminator::Goto(target) => {
                let label = block_labels[target];
                self.enc().jmp_label(label);
            }
            MirTerminator::If {
                cond,
                then_block,
                else_block,
            } => {
                self.load_value_to_reg(cond, Reg64::RAX, func)?;
                self.enc().test_rr(Reg64::RAX, Reg64::RAX);

                let then_label = block_labels[then_block];
                let else_label = block_labels[else_block];

                self.enc().jcc_label(Cond::NE, then_label);
                self.enc().jmp_label(else_label);
            }
            MirTerminator::Return(value) => {
                if let Some(val) = value {
                    self.load_value_to_reg(val, Reg64::RAX, func)?;
                }
                // Epilogue
                self.enc().mov_rr(Reg64::RSP, Reg64::RBP);
                self.enc().pop(Reg64::RBP);
                self.enc().ret();
            }
            MirTerminator::Call {
                func: callee,
                args,
                dest,
                target,
                ..
            } => {
                // Pass arguments
                let arg_regs = [
                    Reg64::RDI,
                    Reg64::RSI,
                    Reg64::RDX,
                    Reg64::RCX,
                    Reg64::R8,
                    Reg64::R9,
                ];
                for (i, arg) in args.iter().enumerate() {
                    if i < arg_regs.len() {
                        self.load_value_to_reg(arg, arg_regs[i], func)?;
                    } else {
                        // Stack arguments (pushed in reverse order)
                        self.load_value_to_reg(arg, Reg64::RAX, func)?;
                        self.enc().push(Reg64::RAX);
                    }
                }

                // Call
                if let MirValue::Function(name) = callee {
                    self.enc().call_symbol(name);
                } else {
                    self.load_value_to_reg(callee, Reg64::RAX, func)?;
                    self.enc().call_reg(Reg64::RAX);
                }

                // Store result
                if let Some(d) = dest {
                    self.store_reg_to_local(Reg64::RAX, *d)?;
                }

                // Jump to continue block
                if let Some(target_block) = target {
                    let label = block_labels[target_block];
                    self.enc().jmp_label(label);
                }
            }
            MirTerminator::Switch {
                value,
                targets,
                default,
            } => {
                self.load_value_to_reg(value, Reg64::RAX, func)?;

                // Generate comparisons and jumps for each case
                for (case_val, case_block) in targets {
                    let v_i32 = match case_val {
                        MirConst::Int(v, _) => *v as i32,
                        MirConst::Uint(v, _) => *v as i32,
                        _ => continue,
                    };
                    self.enc().cmp_ri32(Reg64::RAX, v_i32);
                    let label = block_labels[case_block];
                    self.enc().jcc_label(Cond::E, label);
                }

                // Default case
                let default_label = block_labels[default];
                self.enc().jmp_label(default_label);
            }
            MirTerminator::Unreachable => {
                self.enc().ud2();
            }
            MirTerminator::Abort => {
                self.enc().call_symbol("abort");
            }
            MirTerminator::Drop { target, .. } => {
                // For now, just jump to target (drop glue not implemented)
                let label = block_labels[target];
                self.enc().jmp_label(label);
            }
            MirTerminator::Assert {
                cond,
                expected,
                target,
                ..
            } => {
                self.load_value_to_reg(cond, Reg64::RAX, func)?;
                self.enc().test_rr(Reg64::RAX, Reg64::RAX);
                let label = block_labels[target];
                if *expected {
                    self.enc().jcc_label(Cond::NE, label);
                } else {
                    self.enc().jcc_label(Cond::E, label);
                }
                // If assertion fails, call abort
                self.enc().call_symbol("abort");
            }
            MirTerminator::Resume => {
                // Resume unwinding - call _Unwind_Resume or similar
                self.enc().call_symbol("_Unwind_Resume");
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::codegen::builder::{values, MirBuilder, MirModuleBuilder};

    // =========================================================================
    // X86-64 BACKEND TESTS
    // =========================================================================

    #[test]
    fn test_x86_64_backend_simple() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::i32(), MirType::i32()], MirType::i32());
        let mut builder = MirBuilder::new("add", sig);

        let a = builder.param_local(0);
        let b = builder.param_local(1);
        let result = builder.create_local(MirType::i32());

        builder.binary_op(result, BinOp::Add, values::local(a), values::local(b));
        builder.ret(Some(values::local(result)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains(".intel_syntax noprefix"));
        assert!(code.contains("add:"));
        assert!(code.contains("ret"));
    }

    #[test]
    fn test_x86_64_backend_void_function() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![], MirType::Void);
        let mut builder = MirBuilder::new("noop", sig);
        builder.ret_void();

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains("noop:"));
        assert!(code.contains("push rbp"));
        assert!(code.contains("mov rbp, rsp"));
        assert!(code.contains("ret"));
    }

    #[test]
    fn test_x86_64_backend_prologue_epilogue() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![], MirType::i32());
        let mut builder = MirBuilder::new("test_func", sig);
        builder.ret(Some(values::i32(42)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        // Check prologue
        assert!(code.contains("push rbp"));
        assert!(code.contains("mov rbp, rsp"));
        // Check epilogue
        assert!(code.contains("mov rsp, rbp"));
        assert!(code.contains("pop rbp"));
    }

    #[test]
    fn test_x86_64_backend_branching() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::Bool], MirType::i32());
        let mut builder = MirBuilder::new("branch_test", sig);

        let cond = builder.param_local(0);
        let then_block = builder.create_block();
        let else_block = builder.create_block();

        builder.branch(values::local(cond), then_block, else_block);

        builder.switch_to_block(then_block);
        builder.ret(Some(values::i32(1)));

        builder.switch_to_block(else_block);
        builder.ret(Some(values::i32(0)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains("test rax, rax"));
        assert!(code.contains("jnz"));
        assert!(code.contains("jmp"));
    }

    #[test]
    fn test_x86_64_backend_binary_ops() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::i32(), MirType::i32()], MirType::i32());
        let mut builder = MirBuilder::new("sub_test", sig);

        let a = builder.param_local(0);
        let b = builder.param_local(1);
        let result = builder.create_local(MirType::i32());

        builder.binary_op(result, BinOp::Sub, values::local(a), values::local(b));
        builder.ret(Some(values::local(result)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains("sub rax, rcx"));
    }

    #[test]
    fn test_x86_64_backend_mul() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::i32(), MirType::i32()], MirType::i32());
        let mut builder = MirBuilder::new("mul_test", sig);

        let a = builder.param_local(0);
        let b = builder.param_local(1);
        let result = builder.create_local(MirType::i32());

        builder.binary_op(result, BinOp::Mul, values::local(a), values::local(b));
        builder.ret(Some(values::local(result)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains("imul rax, rcx"));
    }

    #[test]
    fn test_x86_64_backend_comparison() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::i32(), MirType::i32()], MirType::Bool);
        let mut builder = MirBuilder::new("eq_test", sig);

        let a = builder.param_local(0);
        let b = builder.param_local(1);
        let result = builder.create_local(MirType::Bool);

        builder.binary_op(result, BinOp::Eq, values::local(a), values::local(b));
        builder.ret(Some(values::local(result)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains("cmp rax, rcx"));
        assert!(code.contains("sete al"));
    }

    #[test]
    fn test_x86_64_backend_global_variable() {
        let mut module_builder = MirModuleBuilder::new("test");

        let mut global = MirGlobal::new("MY_VAR", MirType::i64());
        global.is_mut = true;
        module_builder.add_global(global);

        let sig = MirFnSig::new(vec![], MirType::Void);
        let mut builder = MirBuilder::new("main", sig);
        builder.ret_void();
        module_builder.add_function(builder.build());

        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains(".section .data"));
        assert!(code.contains("MY_VAR:"));
    }

    #[test]
    fn test_x86_64_backend_string_table() {
        let mut module_builder = MirModuleBuilder::new("test");

        module_builder.intern_string("hello world");

        let sig = MirFnSig::new(vec![], MirType::Void);
        let mut builder = MirBuilder::new("main", sig);
        builder.ret_void();
        module_builder.add_function(builder.build());

        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains(".section .rodata"));
        assert!(code.contains("__str0:"));
        assert!(code.contains(".asciz"));
    }

    #[test]
    fn test_x86_64_backend_target() {
        let backend = X86_64Backend::new();
        assert_eq!(backend.target(), Target::X86_64);
    }

    #[test]
    fn test_x86_64_backend_type_size() {
        let backend = X86_64Backend::new();

        assert_eq!(backend.type_size(&MirType::Void), 0);
        assert_eq!(backend.type_size(&MirType::Bool), 1);
        assert_eq!(backend.type_size(&MirType::i8()), 1);
        assert_eq!(backend.type_size(&MirType::i16()), 2);
        assert_eq!(backend.type_size(&MirType::i32()), 4);
        assert_eq!(backend.type_size(&MirType::i64()), 8);
        assert_eq!(backend.type_size(&MirType::Int(IntSize::I128, true)), 16);
        assert_eq!(backend.type_size(&MirType::f32()), 4);
        assert_eq!(backend.type_size(&MirType::f64()), 8);
        assert_eq!(
            backend.type_size(&MirType::Ptr(Box::new(MirType::i32()))),
            8
        );
    }

    #[test]
    fn test_x86_64_backend_unary_op() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::i32()], MirType::i32());
        let mut builder = MirBuilder::new("negate", sig);

        let a = builder.param_local(0);
        let result = builder.create_local(MirType::i32());

        builder.unary_op(result, UnaryOp::Neg, values::local(a));
        builder.ret(Some(values::local(result)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains("neg rax"));
    }

    #[test]
    fn test_x86_64_backend_public_function() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![], MirType::Void);
        let mut func = MirFunction::new("public_fn", sig);
        func.is_public = true;
        func.add_block(MirBlock::new(BlockId::ENTRY));
        func.block_mut(BlockId::ENTRY)
            .unwrap()
            .set_terminator(MirTerminator::Return(None));
        module_builder.add_function(func);

        let module = module_builder.build();

        let mut backend = X86_64Backend::new();
        let output = backend.generate(&module).unwrap();

        let code = output.as_string().unwrap();
        assert!(code.contains(".globl public_fn"));
    }

    // =========================================================================
    // MACHINE CODE GENERATION TESTS
    // =========================================================================

    #[test]
    fn test_x86_64_machine_code_simple() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![], MirType::i32());
        let mut builder = MirBuilder::new("ret42", sig);
        builder.ret(Some(values::i32(42)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new().with_machine_code();
        let output = backend.generate(&module).unwrap();

        // Check that we got object output
        assert_eq!(output.format, OutputFormat::Object);
        assert!(!output.data.is_empty());

        // The code should contain a prologue (push rbp = 0x55)
        assert!(output.data.contains(&0x55));
    }

    #[test]
    fn test_x86_64_machine_code_add() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::i64(), MirType::i64()], MirType::i64());
        let mut builder = MirBuilder::new("add", sig);

        let a = builder.param_local(0);
        let b = builder.param_local(1);
        let result = builder.create_local(MirType::i64());

        builder.binary_op(result, BinOp::Add, values::local(a), values::local(b));
        builder.ret(Some(values::local(result)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new().with_machine_code();
        let output = backend.generate(&module).unwrap();

        assert_eq!(output.format, OutputFormat::Object);
        assert!(!output.data.is_empty());
    }

    #[test]
    fn test_x86_64_machine_code_with_branch() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::Bool], MirType::i32());
        let mut builder = MirBuilder::new("branch", sig);

        let cond = builder.param_local(0);
        let then_block = builder.create_block();
        let else_block = builder.create_block();

        builder.branch(values::local(cond), then_block, else_block);

        builder.switch_to_block(then_block);
        builder.ret(Some(values::i32(1)));

        builder.switch_to_block(else_block);
        builder.ret(Some(values::i32(0)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new().with_machine_code();
        let output = backend.generate(&module).unwrap();

        assert_eq!(output.format, OutputFormat::Object);
        assert!(!output.data.is_empty());

        // Should contain JMP (0xE9) or JCC (0x0F 0x8x) instructions
        let contains_jmp = output.data.iter().any(|&b| b == 0xE9);
        let contains_jcc = output
            .data
            .windows(2)
            .any(|w| w[0] == 0x0F && (w[1] & 0xF0) == 0x80);
        assert!(contains_jmp || contains_jcc);
    }

    #[test]
    fn test_x86_64_machine_code_prologue_epilogue() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![], MirType::Void);
        let mut builder = MirBuilder::new("noop", sig);
        builder.ret_void();

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new().with_machine_code();
        let output = backend.generate(&module).unwrap();

        // Prologue: push rbp (0x55), mov rbp, rsp (0x48 0x89 0xE5)
        assert!(output.data.starts_with(&[0x55]));

        // Epilogue should end with ret (0xC3)
        assert_eq!(*output.data.last().unwrap(), 0xC3);
    }

    #[test]
    fn test_x86_64_machine_code_binary_ops() {
        let ops = [
            (BinOp::Add, "add"),
            (BinOp::Sub, "sub"),
            (BinOp::Mul, "mul"),
            (BinOp::BitAnd, "and"),
            (BinOp::BitOr, "or"),
            (BinOp::BitXor, "xor"),
        ];

        for (op, name) in ops {
            let mut module_builder = MirModuleBuilder::new("test");

            let sig = MirFnSig::new(vec![MirType::i64(), MirType::i64()], MirType::i64());
            let mut builder = MirBuilder::new(name, sig);

            let a = builder.param_local(0);
            let b = builder.param_local(1);
            let result = builder.create_local(MirType::i64());

            builder.binary_op(result, op, values::local(a), values::local(b));
            builder.ret(Some(values::local(result)));

            module_builder.add_function(builder.build());
            let module = module_builder.build();

            let mut backend = X86_64Backend::new().with_machine_code();
            let output = backend.generate(&module).unwrap();

            assert_eq!(output.format, OutputFormat::Object);
            assert!(!output.data.is_empty(), "Failed for op: {}", name);
        }
    }

    #[test]
    fn test_x86_64_machine_code_comparison() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::i64(), MirType::i64()], MirType::Bool);
        let mut builder = MirBuilder::new("eq", sig);

        let a = builder.param_local(0);
        let b = builder.param_local(1);
        let result = builder.create_local(MirType::Bool);

        builder.binary_op(result, BinOp::Eq, values::local(a), values::local(b));
        builder.ret(Some(values::local(result)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new().with_machine_code();
        let output = backend.generate(&module).unwrap();

        // Should contain SETcc instruction (0x0F 0x9x)
        let has_setcc = output
            .data
            .windows(2)
            .any(|w| w[0] == 0x0F && (w[1] & 0xF0) == 0x90);
        assert!(has_setcc);
    }

    #[test]
    fn test_x86_64_output_mode() {
        let backend = X86_64Backend::new();
        assert_eq!(backend.mode, X86_64OutputMode::Assembly);

        let backend = X86_64Backend::new().with_machine_code();
        assert_eq!(backend.mode, X86_64OutputMode::MachineCode);
        assert!(backend.encoder.is_some());
    }

    #[test]
    fn test_x86_64_machine_code_unary_neg() {
        let mut module_builder = MirModuleBuilder::new("test");

        let sig = MirFnSig::new(vec![MirType::i64()], MirType::i64());
        let mut builder = MirBuilder::new("negate", sig);

        let a = builder.param_local(0);
        let result = builder.create_local(MirType::i64());

        builder.unary_op(result, UnaryOp::Neg, values::local(a));
        builder.ret(Some(values::local(result)));

        module_builder.add_function(builder.build());
        let module = module_builder.build();

        let mut backend = X86_64Backend::new().with_machine_code();
        let output = backend.generate(&module).unwrap();

        assert_eq!(output.format, OutputFormat::Object);
        // NEG is encoded as 0xF7 /3
        assert!(output.data.contains(&0xF7));
    }

    #[test]
    fn test_x86_64_machine_code_multiple_functions() {
        let mut module_builder = MirModuleBuilder::new("test");

        // First function
        let sig1 = MirFnSig::new(vec![], MirType::i32());
        let mut builder1 = MirBuilder::new("func1", sig1);
        builder1.ret(Some(values::i32(1)));
        module_builder.add_function(builder1.build());

        // Second function
        let sig2 = MirFnSig::new(vec![], MirType::i32());
        let mut builder2 = MirBuilder::new("func2", sig2);
        builder2.ret(Some(values::i32(2)));
        module_builder.add_function(builder2.build());

        let module = module_builder.build();

        let mut backend = X86_64Backend::new().with_machine_code();
        let output = backend.generate(&module).unwrap();

        assert_eq!(output.format, OutputFormat::Object);

        // Should contain two function prologues (two push rbp instructions)
        let push_count = output.data.iter().filter(|&&b| b == 0x55).count();
        assert_eq!(push_count, 2);
    }
}
