//! Statement generation for the code generator.
//!
//! This module handles:
//! - Variable declarations (auto and typed)
//! - Return statements
//! - If statements with proper block handling
//! - While and for loops
//! - Expression statements

use inkwell::AddressSpace;
use inkwell::types::{BasicType, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, FunctionValue};

use crate::ast::{
    EnumVariantField, ExpressionKind, ExpressionNode, LiteralNode, PatternNode, PrimitiveType,
    StatementKind, StatementNode, TypeKind,
};
use crate::semantics::{Type, Type as ResolvedType};

use super::CodeGenerator;

impl<'a> CodeGenerator<'a> {
    fn declare_variable(
        &mut self,
        name: &str,
        var_type: BasicTypeEnum<'a>,
        value: BasicValueEnum<'a>,
        resolved_type: &ResolvedType,
        function: Option<&FunctionValue<'a>>,
    ) -> Result<(), String> {
        let existing_var = self.variables.get(name).cloned();

        if let Some((existing_ptr, _, _)) = existing_var {
            if value.is_struct_value() {
                self.builder
                    .build_store(existing_ptr, value)
                    .map_err(|e| e.to_string())?;
            } else {
                let boxed = self.box_value(value);
                self.builder
                    .build_store(existing_ptr, boxed)
                    .map_err(|e| e.to_string())?;
            }
        } else if value.is_struct_value() {
            let alloca = if let Some(func) = function {
                self.create_entry_block_alloca(*func, var_type, name)?
            } else {
                self.builder
                    .build_alloca(var_type, name)
                    .map_err(|e| e.to_string())?
            };
            self.builder
                .build_store(alloca, value)
                .map_err(|e| e.to_string())?;
            self.variables
                .insert(name.to_string(), (alloca, var_type, resolved_type.clone()));
        } else {
            let boxed = self.box_value(value);
            let ptr_type = self.context.ptr_type(AddressSpace::default());
            let alloca = if let Some(func) = function {
                self.create_entry_block_alloca(*func, ptr_type.into(), name)?
            } else {
                self.builder
                    .build_alloca(ptr_type, name)
                    .map_err(|e| e.to_string())?
            };
            self.builder
                .build_store(alloca, boxed)
                .map_err(|e| e.to_string())?;
            self.variables.insert(
                name.to_string(),
                (
                    alloca,
                    BasicTypeEnum::PointerType(ptr_type),
                    resolved_type.clone(),
                ),
            );
            if function.is_some() && self.type_needs_rc_tracking(resolved_type) {
                self.track_rc_variable(name, alloca);
            }
        }
        Ok(())
    }

    fn generate_for_statement_inner(
        &mut self,
        function: &FunctionValue<'a>,
        var: &str,
        var_type: &crate::ast::TypeNode,
        iter: &ExpressionNode,
        body: &[StatementNode],
    ) -> Result<(), String> {
        if let ExpressionKind::Call { func, args } = &iter.kind {
            if let ExpressionKind::Identifier(name) = &func.kind {
                if name == "range" && args.len() == 2 {
                    let resolved_var_type = Type::Primitive(PrimitiveType::Int);
                    let start_val = self.generate_expression(&args[0])?;
                    let end_val = self.generate_expression(&args[1])?;
                    let index_type = self.context.i64_type();
                    let index_alloca = self
                        .builder
                        .build_alloca(index_type, "index")
                        .map_err(|e| e.to_string())?;
                    self.builder
                        .build_store(index_alloca, start_val)
                        .map_err(|e| e.to_string())?;
                    let ptr_type = self.context.ptr_type(AddressSpace::default());
                    let var_alloca = self
                        .builder
                        .build_alloca(ptr_type, var)
                        .map_err(|e| e.to_string())?;
                    self.variables.insert(
                        var.to_string(),
                        (
                            var_alloca,
                            BasicTypeEnum::PointerType(ptr_type),
                            resolved_var_type.clone(),
                        ),
                    );
                    let label_id = self.label_counter;
                    self.label_counter += 1;
                    let header_bb = self
                        .context
                        .append_basic_block(*function, &format!("for_header_{}", label_id));
                    let body_bb = self
                        .context
                        .append_basic_block(*function, &format!("for_body_{}", label_id));
                    let exit_bb = self
                        .context
                        .append_basic_block(*function, &format!("for_exit_{}", label_id));
                    self.builder
                        .build_unconditional_branch(header_bb)
                        .map_err(|e| e.to_string())?;
                    self.builder.position_at_end(header_bb);
                    let index_load = self
                        .builder
                        .build_load(index_type, index_alloca, "index_load")
                        .map_err(|e| e.to_string())?;
                    let cmp = self
                        .builder
                        .build_int_compare(
                            inkwell::IntPredicate::SLT,
                            index_load.into_int_value(),
                            end_val.into_int_value(),
                            "cmp",
                        )
                        .map_err(|e| e.to_string())?;
                    self.builder
                        .build_conditional_branch(cmp, body_bb, exit_bb)
                        .map_err(|e| e.to_string())?;
                    self.builder.position_at_end(body_bb);
                    let index_load2 = self
                        .builder
                        .build_load(index_type, index_alloca, "index_load2")
                        .map_err(|e| e.to_string())?;
                    let boxed = self.box_value(index_load2);
                    self.builder
                        .build_store(var_alloca, boxed)
                        .map_err(|e| e.to_string())?;
                    for stmt in body {
                        self.generate_statement(stmt, Some(function))?;
                    }
                    let one = self.context.i64_type().const_int(1, false);
                    let new_index = self
                        .builder
                        .build_int_add(index_load2.into_int_value(), one, "inc")
                        .map_err(|e| e.to_string())?;
                    self.builder
                        .build_store(index_alloca, new_index)
                        .map_err(|e| e.to_string())?;
                    self.builder
                        .build_unconditional_branch(header_bb)
                        .map_err(|e| e.to_string())?;
                    self.builder.position_at_end(exit_bb);
                    return Ok(());
                }
                return Err("For loop iter must be range(start, end)".to_string());
            }
            return Err("For loop iter must be range call".to_string());
        }

        if let ExpressionKind::Identifier(_) = &iter.kind {
            let resolved_var_type = self
                .analyzer
                .resolve_type(var_type)
                .map_err(|e| e.message)?;
            let list_val = self.generate_expression(iter)?;
            let len_call = self
                .builder
                .build_call(
                    self.runtime_function("mux_value_list_length")
                        .expect("mux_value_list_length must be declared in runtime"),
                    &[list_val.into()],
                    "list_len",
                )
                .map_err(|e| e.to_string())?;
            let len_val = len_call
                .try_as_basic_value()
                .basic()
                .expect("mux_value_list_length should return a basic value")
                .into_int_value();
            let index_type = self.context.i64_type();
            let index_alloca = self
                .builder
                .build_alloca(index_type, "index")
                .map_err(|e| e.to_string())?;
            let zero = self.context.i64_type().const_int(0, false);
            self.builder
                .build_store(index_alloca, zero)
                .map_err(|e| e.to_string())?;
            let ptr_type = self.context.ptr_type(AddressSpace::default());
            let var_alloca = self
                .builder
                .build_alloca(ptr_type, var)
                .map_err(|e| e.to_string())?;
            self.variables.insert(
                var.to_string(),
                (
                    var_alloca,
                    BasicTypeEnum::PointerType(ptr_type),
                    resolved_var_type.clone(),
                ),
            );
            let label_id = self.label_counter;
            self.label_counter += 1;
            let header_bb = self
                .context
                .append_basic_block(*function, &format!("for_header_{}", label_id));
            let body_bb = self
                .context
                .append_basic_block(*function, &format!("for_body_{}", label_id));
            let exit_bb = self
                .context
                .append_basic_block(*function, &format!("for_exit_{}", label_id));
            self.builder
                .build_unconditional_branch(header_bb)
                .map_err(|e| e.to_string())?;
            self.builder.position_at_end(header_bb);
            let index_load = self
                .builder
                .build_load(index_type, index_alloca, "index_load")
                .map_err(|e| e.to_string())?;
            let cmp = self
                .builder
                .build_int_compare(
                    inkwell::IntPredicate::SLT,
                    index_load.into_int_value(),
                    len_val,
                    "cmp",
                )
                .map_err(|e| e.to_string())?;
            self.builder
                .build_conditional_branch(cmp, body_bb, exit_bb)
                .map_err(|e| e.to_string())?;
            self.builder.position_at_end(body_bb);
            let index_load2 = self
                .builder
                .build_load(index_type, index_alloca, "index_load2")
                .map_err(|e| e.to_string())?;
            let get_call = self
                .builder
                .build_call(
                    self.runtime_function("mux_value_list_get_value")
                        .expect("mux_value_list_get_value must be declared in runtime"),
                    &[list_val.into(), index_load2.into()],
                    "list_get_value",
                )
                .map_err(|e| e.to_string())?;
            let value_ptr = get_call
                .try_as_basic_value()
                .basic()
                .expect("mux_value_list_get_value should return a basic value")
                .into_pointer_value();
            self.builder
                .build_store(var_alloca, value_ptr)
                .map_err(|e| e.to_string())?;
            for stmt in body {
                self.generate_statement(stmt, Some(function))?;
            }
            let one = self.context.i64_type().const_int(1, false);
            let new_index = self
                .builder
                .build_int_add(index_load2.into_int_value(), one, "inc")
                .map_err(|e| e.to_string())?;
            self.builder
                .build_store(index_alloca, new_index)
                .map_err(|e| e.to_string())?;
            self.builder
                .build_unconditional_branch(header_bb)
                .map_err(|e| e.to_string())?;
            let continue_bb = self
                .context
                .append_basic_block(*function, &format!("for_continue_{}", label_id));
            self.builder.position_at_end(exit_bb);
            self.builder
                .build_unconditional_branch(continue_bb)
                .map_err(|e| e.to_string())?;
            self.builder.position_at_end(continue_bb);
            return Ok(());
        }

        Err("For loop iter must be range(...) or list identifier".to_string())
    }

    fn prepare_match_expression(
        &mut self,
        expr: &ExpressionNode,
    ) -> Result<(BasicValueEnum<'a>, ExpressionNode), String> {
        if matches!(
            &expr.kind,
            ExpressionKind::Identifier(_) | ExpressionKind::FieldAccess { .. }
        ) || matches!(&expr.kind, ExpressionKind::Call { func, .. } if matches!(func.kind, ExpressionKind::Identifier(_)))
        {
            return Ok((self.generate_expression(expr)?, expr.clone()));
        }

        let temp_val = self.generate_expression(expr)?;
        let temp_name = format!("match_temp_{}", self.label_counter);
        self.label_counter += 1;
        let temp_type = self.context.ptr_type(AddressSpace::default());
        let temp_alloca = self
            .builder
            .build_alloca(temp_type, &temp_name)
            .map_err(|e| e.to_string())?;
        self.builder
            .build_store(temp_alloca, temp_val)
            .map_err(|e| e.to_string())?;
        let actual_type = self
            .get_resolved_expression_type(expr)
            .map_err(|e| format!("Type inference failed: {}", e))?;
        self.variables.insert(
            temp_name.clone(),
            (
                temp_alloca,
                BasicTypeEnum::PointerType(temp_type),
                actual_type,
            ),
        );
        let temp_expr = ExpressionNode {
            kind: ExpressionKind::Identifier(temp_name),
            span: expr.span,
        };
        Ok((temp_val, temp_expr))
    }

    fn resolve_match_expression_type(
        &mut self,
        match_expr: &ExpressionNode,
    ) -> Result<Type, String> {
        if let Some(t) = self.resolve_match_identifier_type(match_expr) {
            return t;
        }

        if let Some(t) = self.resolve_match_self_field_type(match_expr) {
            return t;
        }

        self.get_resolved_expression_type(match_expr)
            .map_err(|e| format!("Type inference failed: {}", e))
    }

    fn resolve_match_identifier_type(
        &mut self,
        match_expr: &ExpressionNode,
    ) -> Option<Result<Type, String>> {
        let ExpressionKind::Identifier(name) = &match_expr.kind else {
            return None;
        };

        if name == "self" || name.starts_with("match_temp_") {
            return Some(
                self.variables
                    .get(name)
                    .or_else(|| self.global_variables.get(name))
                    .map(|(_, _, var_type)| var_type.clone())
                    .ok_or_else(|| {
                        let label = if name == "self" {
                            "Self"
                        } else {
                            "Temporary variable"
                        };
                        format!("{} {} not found", label, name)
                    }),
            );
        }

        None
    }

    fn resolve_match_self_field_type(
        &mut self,
        match_expr: &ExpressionNode,
    ) -> Option<Result<Type, String>> {
        let ExpressionKind::FieldAccess { expr, field } = &match_expr.kind else {
            return None;
        };
        let ExpressionKind::Identifier(obj) = &expr.kind else {
            return None;
        };
        if obj != "self" {
            return None;
        }

        let self_type = match self
            .variables
            .get("self")
            .or_else(|| self.global_variables.get("self"))
        {
            Some((_, _, t)) => t.clone(),
            None => return Some(Err("Self not found".to_string())),
        };

        let result = match &self_type {
            Type::Named(class_name, _) => self
                .classes
                .get(class_name)
                .cloned()
                .ok_or_else(|| format!("Class {} not found", class_name))
                .and_then(|fields| {
                    fields
                        .iter()
                        .find(|f| f.name == *field)
                        .ok_or_else(|| format!("Field {} not found in class {}", field, class_name))
                        .and_then(|f| {
                            self.analyzer
                                .resolve_type(&f.type_)
                                .map_err(|e| format!("Type resolution failed: {}", e))
                        })
                }),
            _ => self
                .get_resolved_expression_type(match_expr)
                .map_err(|e| format!("Type inference failed: {}", e)),
        };
        Some(result)
    }

    fn generate_match_statement_inner(
        &mut self,
        function: &FunctionValue<'a>,
        expr: &ExpressionNode,
        arms: &[crate::ast::MatchArm],
    ) -> Result<(), String> {
        let (expr_val, match_expr) = self.prepare_match_expression(expr)?;
        let match_expr_type = self.resolve_match_expression_type(&match_expr)?;
        let is_enum = self.is_enum_match_type(&match_expr_type);

        if is_enum {
            self.generate_enum_match(function, &match_expr, &match_expr_type, expr_val, arms)?;
        } else {
            self.generate_switch_match(function, &match_expr_type, expr_val, arms)?;
        }

        if let ExpressionKind::Identifier(temp_name) = &match_expr.kind
            && temp_name.starts_with("match_temp_")
        {
            self.variables.remove(temp_name);
        }

        Ok(())
    }

    fn generate_auto_decl_statement(
        &mut self,
        name: &str,
        expr: &ExpressionNode,
        function: Option<&FunctionValue<'a>>,
    ) -> Result<(), String> {
        let value = self.generate_expression(expr)?;
        let resolved_type = self
            .resolve_expression_type_with_fallback(expr)
            .map_err(|e| format!("Failed to get type for {}: {}", name, e))?;
        let concrete_type = self
            .resolve_type(&resolved_type)
            .unwrap_or_else(|_| resolved_type.clone());
        let var_type = value.get_type();
        self.declare_variable(name, var_type, value, &concrete_type, function)
    }

    fn generate_typed_decl_statement(
        &mut self,
        name: &str,
        type_node: &crate::ast::TypeNode,
        expr: &ExpressionNode,
        function: Option<&FunctionValue<'a>>,
    ) -> Result<(), String> {
        let var_type = self.llvm_type_from_mux_type(type_node)?;
        let value = self.generate_expression(expr)?;
        let resolved_type = self
            .analyzer
            .resolve_type(type_node)
            .map_err(|e| format!("Failed to resolve type for {}: {}", name, e.message))?;
        self.declare_variable(name, var_type, value, &resolved_type, function)
    }

    fn generate_const_decl_statement(
        &mut self,
        name: &str,
        expr: &ExpressionNode,
        function: Option<&FunctionValue<'a>>,
    ) -> Result<(), String> {
        let value = self.generate_expression(expr)?;
        let boxed = self.box_value(value);
        let ptr_type = self.context.ptr_type(AddressSpace::default());
        let resolved_type = self
            .resolve_expression_type_with_fallback(expr)
            .map_err(|e| format!("Failed to get type for {}: {}", name, e))?;
        let existing_ptr = self.variables.get(name).map(|(p, _, _)| *p);
        if let Some(existing_ptr) = existing_ptr {
            self.builder
                .build_store(existing_ptr, boxed)
                .map_err(|e| e.to_string())?;
            return Ok(());
        }

        let alloca = if let Some(func) = function {
            self.create_entry_block_alloca(*func, ptr_type.into(), name)?
        } else {
            self.builder
                .build_alloca(ptr_type, name)
                .map_err(|e| e.to_string())?
        };
        self.builder
            .build_store(alloca, boxed)
            .map_err(|e| e.to_string())?;
        self.variables.insert(
            name.to_string(),
            (
                alloca,
                BasicTypeEnum::PointerType(ptr_type),
                resolved_type.clone(),
            ),
        );
        if function.is_some() && self.type_needs_rc_tracking(&resolved_type) {
            self.track_rc_variable(name, alloca);
        }

        Ok(())
    }

    fn try_generate_bool_literal_return(&mut self, expr: &ExpressionNode) -> Result<bool, String> {
        if let ExpressionKind::Literal(LiteralNode::Boolean(b)) = &expr.kind
            && let Some(ResolvedType::Primitive(PrimitiveType::Bool)) =
                &self.current_function_return_type
        {
            self.generate_all_scopes_cleanup()?;
            let bool_val = self
                .context
                .bool_type()
                .const_int(if *b { 1 } else { 0 }, false);
            self.builder
                .build_return(Some(&bool_val))
                .map_err(|e| e.to_string())?;
            return Ok(true);
        }

        Ok(false)
    }

    fn return_bool_int_value(
        &mut self,
        int_val: inkwell::values::IntValue<'a>,
    ) -> Result<(), String> {
        self.generate_all_scopes_cleanup()?;
        if int_val.get_type().get_bit_width() == 1 {
            self.builder
                .build_return(Some(&int_val))
                .map_err(|e| e.to_string())?;
            return Ok(());
        }

        let bool_val = self
            .builder
            .build_int_truncate(int_val, self.context.bool_type(), "int_to_i1")
            .map_err(|e| e.to_string())?;
        self.builder
            .build_return(Some(&bool_val))
            .map_err(|e| e.to_string())?;
        Ok(())
    }

    fn return_bool_ptr_value(
        &mut self,
        ptr: inkwell::values::PointerValue<'a>,
    ) -> Result<(), String> {
        let get_bool_fn = self
            .runtime_function("mux_value_get_bool")
            .ok_or("mux_value_get_bool not found")?;
        let result = self
            .builder
            .build_call(get_bool_fn, &[ptr.into()], "get_bool")
            .map_err(|e| e.to_string())?
            .try_as_basic_value()
            .basic()
            .ok_or("Call returned no value")?;
        self.generate_all_scopes_cleanup()?;
        let bool_val = self
            .builder
            .build_int_truncate(
                result.into_int_value(),
                self.context.bool_type(),
                "i32_to_i1",
            )
            .map_err(|e| e.to_string())?;
        self.builder
            .build_return(Some(&bool_val))
            .map_err(|e| e.to_string())?;
        Ok(())
    }

    fn return_bool_value(&mut self, value: BasicValueEnum<'a>) -> Result<(), String> {
        if value.is_int_value() {
            return self.return_bool_int_value(value.into_int_value());
        }
        if value.is_pointer_value() {
            return self.return_bool_ptr_value(value.into_pointer_value());
        }
        Err("Expected bool value or pointer".to_string())
    }

    fn return_list_value(&mut self, value: BasicValueEnum<'a>) -> Result<(), String> {
        if !value.is_pointer_value() {
            return Err("Expected pointer value for list return".to_string());
        }
        self.rc_inc_if_pointer(value)?;
        self.generate_all_scopes_cleanup()?;
        self.builder
            .build_return(Some(&value))
            .map_err(|e| e.to_string())?;
        Ok(())
    }

    fn return_boxed_complex_value(&mut self, value: BasicValueEnum<'a>) -> Result<(), String> {
        let boxed = match value {
            BasicValueEnum::PointerValue(_) => value,
            _ => self.box_value(value).into(),
        };
        self.rc_inc_if_pointer(boxed)?;
        self.generate_all_scopes_cleanup()?;
        self.builder
            .build_return(Some(&boxed))
            .map_err(|e| e.to_string())?;
        Ok(())
    }

    fn generate_typed_return(
        &mut self,
        return_type: ResolvedType,
        value: BasicValueEnum<'a>,
    ) -> Result<(), String> {
        match return_type {
            ResolvedType::Primitive(PrimitiveType::Int) => {
                let raw = self.get_raw_int_value(value)?;
                self.generate_all_scopes_cleanup()?;
                self.builder
                    .build_return(Some(&raw))
                    .map_err(|e| e.to_string())?;
                Ok(())
            }
            ResolvedType::Primitive(PrimitiveType::Float) => {
                let raw = self.get_raw_float_value(value)?;
                self.generate_all_scopes_cleanup()?;
                self.builder
                    .build_return(Some(&raw))
                    .map_err(|e| e.to_string())?;
                Ok(())
            }
            ResolvedType::Primitive(PrimitiveType::Bool) => self.return_bool_value(value),
            ResolvedType::List(_) => self.return_list_value(value),
            _ => self.return_boxed_complex_value(value),
        }
    }

    fn generate_return_with_value(&mut self, expr: &ExpressionNode) -> Result<(), String> {
        if self.try_generate_bool_literal_return(expr)? {
            return Ok(());
        }

        let value = self.generate_expression(expr)?;
        if let Some(return_type) = self.current_function_return_type.clone() {
            return self.generate_typed_return(return_type, value);
        }

        let boxed = self.box_value(value);
        self.rc_inc_if_pointer(boxed.into())?;
        self.generate_all_scopes_cleanup()?;
        self.builder
            .build_return(Some(&boxed))
            .map_err(|e| e.to_string())?;
        Ok(())
    }

    fn generate_if_statement(
        &mut self,
        function: &FunctionValue<'a>,
        cond: &ExpressionNode,
        then_block: &[StatementNode],
        else_block: &Option<Vec<StatementNode>>,
    ) -> Result<(), String> {
        let cond_val = self.generate_expression(cond)?;
        let cond_int = cond_val.into_int_value();
        let if_id = self.label_counter;
        self.label_counter += 1;
        let then_bb = self
            .context
            .append_basic_block(*function, &format!("if_then_{}", if_id));
        let else_bb = self
            .context
            .append_basic_block(*function, &format!("if_else_{}", if_id));
        let then_ends_with_return = then_block
            .last()
            .is_some_and(|s| matches!(s.kind, StatementKind::Return(_)));
        let else_ends_with_return = if let Some(else_stmts) = else_block {
            else_stmts
                .last()
                .is_some_and(|s| matches!(s.kind, StatementKind::Return(_)))
        } else {
            false
        };
        let needs_merge = !then_ends_with_return || !else_ends_with_return;
        let merge_bb = if needs_merge {
            Some(
                self.context
                    .append_basic_block(*function, &format!("if_merge_{}", if_id)),
            )
        } else {
            None
        };
        self.builder
            .build_conditional_branch(cond_int, then_bb, else_bb)
            .map_err(|e| e.to_string())?;

        self.builder.position_at_end(then_bb);
        for stmt in then_block {
            self.generate_statement(stmt, Some(function))?;
        }
        if !then_ends_with_return && let Some(merge_bb) = merge_bb {
            self.builder
                .build_unconditional_branch(merge_bb)
                .map_err(|e| e.to_string())?;
        }

        self.builder.position_at_end(else_bb);
        if let Some(else_stmts) = else_block {
            for stmt in else_stmts {
                self.generate_statement(stmt, Some(function))?;
            }
        }
        if !else_ends_with_return && let Some(merge_bb) = merge_bb {
            self.builder
                .build_unconditional_branch(merge_bb)
                .map_err(|e| e.to_string())?;
        }

        if let Some(merge_bb) = merge_bb {
            self.builder.position_at_end(merge_bb);
        }

        Ok(())
    }

    fn generate_while_statement(
        &mut self,
        function: &FunctionValue<'a>,
        cond: &ExpressionNode,
        body: &[StatementNode],
    ) -> Result<(), String> {
        let header_bb = self.context.append_basic_block(*function, "while_header");
        let body_bb = self.context.append_basic_block(*function, "while_body");
        let exit_bb = self.context.append_basic_block(*function, "while_exit");
        self.builder
            .build_unconditional_branch(header_bb)
            .map_err(|e| e.to_string())?;

        self.builder.position_at_end(header_bb);
        let cond_val = self.generate_expression(cond)?;
        let cond_int = cond_val.into_int_value();
        self.builder
            .build_conditional_branch(cond_int, body_bb, exit_bb)
            .map_err(|e| e.to_string())?;

        self.builder.position_at_end(body_bb);
        for stmt in body {
            self.generate_statement(stmt, Some(function))?;
        }
        self.builder
            .build_unconditional_branch(header_bb)
            .map_err(|e| e.to_string())?;

        self.builder.position_at_end(exit_bb);
        Ok(())
    }

    fn generate_nested_function_statement(
        &mut self,
        func: &crate::ast::FunctionNode,
        function: Option<&FunctionValue<'a>>,
    ) -> Result<(), String> {
        let parent_name = self
            .current_function_name
            .as_ref()
            .ok_or("Nested function outside of parent function")?;
        let mangled_name = format!("{}!{}", parent_name, func.name);

        self.function_nodes
            .insert(mangled_name.clone(), func.clone());

        if !self.functions.contains_key(&mangled_name) {
            let saved_insert_block = self.builder.get_insert_block();

            let return_type = self
                .analyzer
                .resolve_type(&func.return_type)
                .map_err(|e| e.to_string())?;
            let llvm_return_type = if matches!(return_type, Type::Void) {
                None
            } else {
                Some(self.llvm_type_from_mux_type(&func.return_type)?)
            };

            let mut param_types = vec![];
            for param in &func.params {
                param_types.push(self.llvm_type_from_mux_type(&param.type_)?.into());
            }

            let fn_type = if let Some(ret_type) = llvm_return_type {
                ret_type.fn_type(&param_types, false)
            } else {
                self.context.void_type().fn_type(&param_types, false)
            };

            let declared_function = self.module.add_function(&mangled_name, fn_type, None);
            self.functions
                .insert(mangled_name.clone(), declared_function);

            if let Some(block) = saved_insert_block {
                self.builder.position_at_end(block);
            }
        }

        let saved_function_name = self.current_function_name.clone();
        let saved_function_return_type = self.current_function_return_type.clone();
        let saved_variables = self.variables.clone();

        let mut mangled_func = func.clone();
        mangled_func.name = mangled_name.clone();
        self.generate_function(&mangled_func)?;

        self.current_function_name = saved_function_name;
        self.current_function_return_type = saved_function_return_type;
        self.variables = saved_variables;

        if let Some(current_fn) = function
            && let Some(block) = current_fn.get_last_basic_block()
        {
            self.builder.position_at_end(block);
        }

        Ok(())
    }

    pub(super) fn generate_statement(
        &mut self,
        stmt: &StatementNode,
        function: Option<&FunctionValue<'a>>,
    ) -> Result<(), String> {
        match &stmt.kind {
            StatementKind::AutoDecl(name, _, expr) => {
                self.generate_auto_decl_statement(name, expr, function)?;
            }
            StatementKind::TypedDecl(name, type_node, expr) => {
                self.generate_typed_decl_statement(name, type_node, expr, function)?;
            }
            StatementKind::ConstDecl(name, _, expr) => {
                self.generate_const_decl_statement(name, expr, function)?;
            }
            StatementKind::Return(Some(expr)) => {
                self.generate_return_with_value(expr)?;
            }
            StatementKind::Return(None) => {
                self.generate_all_scopes_cleanup()?;
                self.builder.build_return(None).map_err(|e| e.to_string())?;
            }
            StatementKind::If {
                cond,
                then_block,
                else_block,
            } => {
                let function = function.ok_or("If statement not in function")?;
                self.generate_if_statement(function, cond, then_block, else_block)?;
            }
            StatementKind::While { cond, body } => {
                let function = function.ok_or("While statement not in function")?;
                self.generate_while_statement(function, cond, body)?;
            }
            StatementKind::For {
                var,
                var_type,
                iter,
                body,
            } => {
                let function = function.ok_or("For statement not in function")?;
                self.generate_for_statement_inner(function, var, var_type, iter, body)?;
            }
            StatementKind::Match { expr, arms } => {
                let function = function.ok_or("Match not in function")?;
                self.generate_match_statement_inner(function, expr, arms)?;
            }
            StatementKind::Expression(expr) => {
                self.generate_expression(expr)?;
            }
            StatementKind::Function(func) => {
                self.generate_nested_function_statement(func, function)?;
            }
            _ => {} // skip other statement types for now
        }
        Ok(())
    }

    /// Determines if a type should use enum-based (discriminant) matching.
    fn is_enum_match_type(&self, match_type: &Type) -> bool {
        match match_type {
            Type::Optional(_) | Type::Result(_, _) => true,
            Type::Named(name, _) => {
                // Check if it's an enum in the enum_variants map or symbol table
                if self.enum_variants.contains_key(name) {
                    return true;
                }
                if let Some(symbol) = self.analyzer.symbol_table().lookup(name) {
                    return symbol.kind == crate::semantics::SymbolKind::Enum;
                }
                false
            }
            _ => false,
        }
    }

    fn resolve_enum_match_name(&self, match_expr: &ExpressionNode) -> Result<String, String> {
        match &match_expr.kind {
            ExpressionKind::Identifier(name) => self.resolve_enum_match_name_from_identifier(name),
            ExpressionKind::FieldAccess { expr, field } => {
                self.resolve_enum_match_name_from_field_access(expr, field)
            }
            ExpressionKind::Call { func, .. } => {
                if let ExpressionKind::Identifier(constructor_name) = &func.kind {
                    return self.resolve_enum_constructor_match_name(constructor_name);
                }

                Err("Match expression constructor calls must be simple identifiers".to_string())
            }
            _ => Err(
                "Match expression must be identifier, field access, or constructor call"
                    .to_string(),
            ),
        }
    }

    fn enum_name_from_type(
        &self,
        value_type: &Type,
        unknown_type_msg: &str,
    ) -> Result<String, String> {
        match value_type {
            Type::Named(n, _) => Ok(n.clone()),
            Type::Optional(_) => Ok("optional".to_string()),
            Type::Result(_, _) => Ok("result".to_string()),
            _ => Err(unknown_type_msg.to_string()),
        }
    }

    fn resolve_enum_match_name_from_identifier(&self, name: &str) -> Result<String, String> {
        if let Some((_, _, var_type)) = self.variables.get(name) {
            let unknown_msg = if name.starts_with("match_temp_") {
                format!("Match expression must be an enum type, got {:?}", var_type)
            } else {
                "Match expression must be an enum type".to_string()
            };
            return self.enum_name_from_type(var_type, &unknown_msg);
        }

        if name.starts_with("match_temp_") {
            return Err(format!("Temporary variable {} not found", name));
        }

        if let Some(symbol) = self.analyzer.symbol_table().lookup(name) {
            if let Some(symbol_type) = &symbol.type_ {
                return self
                    .enum_name_from_type(symbol_type, "Match expression must be an enum type");
            }
            return Err("Match expression must be an enum type".to_string());
        }

        Err(format!("Symbol {} not found", name))
    }

    fn resolve_enum_constructor_match_name(
        &self,
        constructor_name: &str,
    ) -> Result<String, String> {
        match constructor_name {
            "some" | "none" => Ok("optional".to_string()),
            "ok" | "err" => Ok("result".to_string()),
            _ => {
                if let Some(symbol) = self.analyzer.symbol_table().lookup(constructor_name) {
                    if let Some(Type::Named(type_name, _)) = &symbol.type_ {
                        Ok(type_name.clone())
                    } else {
                        Err("Constructor must be enum type".to_string())
                    }
                } else {
                    Err(format!("Constructor {} not found", constructor_name))
                }
            }
        }
    }

    fn resolve_enum_match_name_from_field_access(
        &self,
        expr: &ExpressionNode,
        field: &str,
    ) -> Result<String, String> {
        let ExpressionKind::Identifier(obj) = &expr.kind else {
            return Err(
                "Match expression must be identifier, self.field, or obj.field".to_string(),
            );
        };

        if obj == "self" {
            if let Some((_, _, Type::Named(class_name, _))) = self
                .variables
                .get("self")
                .or_else(|| self.global_variables.get("self"))
            {
                return self.resolve_enum_name_from_class_field(class_name, field);
            }
            return Err("Self not found".to_string());
        }

        if let Some((_, _, Type::Named(class_name, _))) = self
            .variables
            .get(obj)
            .or_else(|| self.global_variables.get(obj))
        {
            return self.resolve_enum_name_from_class_field(class_name, field);
        }

        if self
            .variables
            .get(obj)
            .or_else(|| self.global_variables.get(obj))
            .is_some()
        {
            return Err(format!("Variable {} is not a class instance", obj));
        }

        Err(format!("Variable {} not found", obj))
    }

    fn resolve_enum_name_from_class_field(
        &self,
        class_name: &str,
        field: &str,
    ) -> Result<String, String> {
        let fields = self
            .classes
            .get(class_name)
            .ok_or_else(|| format!("Class {} not found", class_name))?;
        let field_info = fields
            .iter()
            .find(|f| f.name == field)
            .ok_or_else(|| format!("Field {} not found in class {}", field, class_name))?;
        if let TypeKind::Named(n, _) = &field_info.type_.kind {
            return Ok(n.clone());
        }
        Err("Match field must be enum type".to_string())
    }

    fn emit_match_guard_and_body(
        &mut self,
        function: &FunctionValue<'a>,
        arm: &crate::ast::MatchArm,
        next_bb: inkwell::basic_block::BasicBlock<'a>,
        end_bb: inkwell::basic_block::BasicBlock<'a>,
        arm_index: usize,
    ) -> Result<(), String> {
        if let Some(guard) = &arm.guard {
            let guard_val = self.generate_expression(guard)?;
            let guard_pass_bb = self
                .context
                .append_basic_block(*function, &format!("match_guard_pass_{}", arm_index));
            self.builder
                .build_conditional_branch(guard_val.into_int_value(), guard_pass_bb, next_bb)
                .map_err(|e| e.to_string())?;
            self.builder.position_at_end(guard_pass_bb);
        }

        for stmt in &arm.body {
            self.generate_statement(stmt, Some(function))?;
        }
        if self
            .builder
            .get_insert_block()
            .and_then(|bb| bb.get_terminator())
            .is_none()
        {
            self.builder
                .build_unconditional_branch(end_bb)
                .map_err(|e| e.to_string())?;
        }

        Ok(())
    }

    fn evaluate_switch_pattern_condition(
        &mut self,
        match_val: BasicValueEnum<'a>,
        match_expr_type: &Type,
        pattern: &PatternNode,
    ) -> Result<inkwell::values::IntValue<'a>, String> {
        match pattern {
            PatternNode::Literal(lit) => {
                let pattern_val = self.generate_literal(lit)?;
                self.generate_value_equality(match_val, pattern_val, match_expr_type)
            }
            PatternNode::Identifier(name) => {
                let is_constant = self
                    .analyzer
                    .symbol_table()
                    .lookup(name)
                    .map(|s| s.kind == crate::semantics::SymbolKind::Constant)
                    .unwrap_or(false);

                if is_constant {
                    let const_ptr = self
                        .variables
                        .get(name)
                        .or_else(|| self.global_variables.get(name))
                        .ok_or_else(|| format!("Constant {} not found", name))?
                        .0;

                    let boxed_ptr = self
                        .builder
                        .build_load(
                            self.context.ptr_type(AddressSpace::default()),
                            const_ptr,
                            &format!("load_{}", name),
                        )
                        .map_err(|e| e.to_string())?
                        .into_pointer_value();

                    let const_val = match match_expr_type {
                        Type::Primitive(PrimitiveType::Int)
                        | Type::Primitive(PrimitiveType::Char) => {
                            self.get_raw_int_value(boxed_ptr.into())?.into()
                        }
                        Type::Primitive(PrimitiveType::Bool) => {
                            self.get_raw_bool_value(boxed_ptr.into())?.into()
                        }
                        Type::Primitive(PrimitiveType::Float) => {
                            self.get_raw_float_value(boxed_ptr.into())?.into()
                        }
                        Type::Primitive(PrimitiveType::Str) => boxed_ptr.into(),
                        _ => boxed_ptr.into(),
                    };

                    self.generate_value_equality(match_val, const_val, match_expr_type)
                } else {
                    let boxed = self.box_value(match_val);
                    let ptr_type = self.context.ptr_type(AddressSpace::default());
                    let alloca = self
                        .builder
                        .build_alloca(ptr_type, name)
                        .map_err(|e| e.to_string())?;
                    self.builder
                        .build_store(alloca, boxed)
                        .map_err(|e| e.to_string())?;
                    self.variables.insert(
                        name.clone(),
                        (alloca, ptr_type.into(), match_expr_type.clone()),
                    );
                    Ok(self.context.bool_type().const_int(1, false))
                }
            }
            PatternNode::Wildcard => Ok(self.context.bool_type().const_int(1, false)),
            PatternNode::List { elements, rest } => self.generate_list_pattern_check(
                match_val,
                match_expr_type,
                elements,
                rest.as_deref(),
            ),
            PatternNode::EnumVariant { .. } => {
                Err("Enum variant patterns are not valid in non-enum match".to_string())
            }
        }
    }

    fn enum_expr_ptr_for_payload_access(
        &mut self,
        enum_name: &str,
        expr_val: BasicValueEnum<'a>,
    ) -> Result<Option<inkwell::values::PointerValue<'a>>, String> {
        if enum_name != "optional" && enum_name != "result" {
            return Ok(None);
        }

        if expr_val.is_pointer_value() {
            return Ok(Some(expr_val.into_pointer_value()));
        }

        let struct_val = expr_val.into_struct_value();
        let alloca = self
            .builder
            .build_alloca(struct_val.get_type(), "temp_enum")
            .map_err(|e| e.to_string())?;
        self.builder
            .build_store(alloca, struct_val)
            .map_err(|e| e.to_string())?;
        Ok(Some(alloca))
    }

    fn enum_temp_struct_ptr(
        &mut self,
        enum_name: &str,
        expr_val: BasicValueEnum<'a>,
    ) -> Result<Option<inkwell::values::PointerValue<'a>>, String> {
        if expr_val.is_pointer_value() {
            return Ok(Some(expr_val.into_pointer_value()));
        }

        let struct_type = self
            .type_map
            .get(enum_name)
            .ok_or_else(|| format!("Enum {} not found in type map", enum_name))?
            .into_struct_type();
        let temp_ptr = self
            .builder
            .build_alloca(struct_type, "temp_enum_struct")
            .map_err(|e| e.to_string())?;
        self.builder
            .build_store(temp_ptr, expr_val)
            .map_err(|e| e.to_string())?;
        Ok(Some(temp_ptr))
    }

    fn enum_pattern_matches(
        &mut self,
        enum_name: &str,
        discriminant: inkwell::values::IntValue<'a>,
        pattern: &PatternNode,
    ) -> Result<inkwell::values::IntValue<'a>, String> {
        match pattern {
            PatternNode::EnumVariant { name, args: _ } => {
                let variant_index = self.get_variant_index(enum_name, name)?;
                self.build_discriminant_comparison(discriminant, variant_index)
            }
            PatternNode::Identifier(_)
            | PatternNode::Literal(_)
            | PatternNode::Wildcard
            | PatternNode::List { .. } => Ok(self.context.bool_type().const_int(1, false)),
        }
    }

    fn bind_builtin_enum_variant_args(
        &mut self,
        enum_name: &str,
        variant_name: &str,
        args: &[PatternNode],
        expr_ptr: inkwell::values::PointerValue<'a>,
        match_expr_type: &Type,
    ) -> Result<(), String> {
        if !matches!(variant_name, "some" | "ok" | "err") || args.is_empty() {
            return Ok(());
        }

        let PatternNode::Identifier(var) = &args[0] else {
            return Ok(());
        };

        let data_func = self.enum_data_function_name(enum_name)?;

        let func = self
            .runtime_function(data_func)
            .ok_or(format!("{} not found", data_func))?;
        let data_call = self
            .builder
            .build_call(func, &[expr_ptr.into()], "data_call")
            .map_err(|e| e.to_string())?;
        let data_ptr = data_call
            .try_as_basic_value()
            .basic()
            .expect("data function should return a basic value")
            .into_pointer_value();

        let (data_val, resolved_type) = self.extract_builtin_enum_variant_value(
            enum_name,
            variant_name,
            match_expr_type,
            data_ptr,
        )?;

        let boxed = self.box_value(data_val);
        let ptr_type = self.context.ptr_type(AddressSpace::default());
        let alloca = self.create_entry_alloca(ptr_type.into(), var)?;
        self.builder
            .build_store(alloca, boxed)
            .map_err(|e| e.to_string())?;

        self.variables
            .insert(var.clone(), (alloca, ptr_type.into(), resolved_type));
        Ok(())
    }

    fn enum_data_function_name(&self, enum_name: &str) -> Result<&'static str, String> {
        match enum_name {
            "optional" => Ok("mux_optional_data"),
            "result" => Ok("mux_result_data"),
            _ => Err(format!("Unknown enum {}", enum_name)),
        }
    }

    fn extract_builtin_enum_variant_value(
        &mut self,
        enum_name: &str,
        variant_name: &str,
        match_expr_type: &Type,
        data_ptr: inkwell::values::PointerValue<'a>,
    ) -> Result<(BasicValueEnum<'a>, Type), String> {
        if enum_name == "optional" {
            return self.extract_optional_variant_value(match_expr_type, data_ptr);
        }
        if enum_name == "result" {
            return self.extract_result_variant_value(variant_name, match_expr_type, data_ptr);
        }
        Err(format!("Unknown enum {}", enum_name))
    }

    fn extract_optional_variant_value(
        &mut self,
        match_expr_type: &Type,
        data_ptr: inkwell::values::PointerValue<'a>,
    ) -> Result<(BasicValueEnum<'a>, Type), String> {
        if let Type::Optional(inner_type) = match_expr_type {
            return self.extract_value_from_ptr(data_ptr, inner_type, "some");
        }
        Err(format!(
            "Type mismatch: expected Optional, got {:?}",
            match_expr_type
        ))
    }

    fn extract_result_variant_value(
        &mut self,
        variant_name: &str,
        match_expr_type: &Type,
        data_ptr: inkwell::values::PointerValue<'a>,
    ) -> Result<(BasicValueEnum<'a>, Type), String> {
        if let Type::Result(ok_type, err_type) = match_expr_type {
            let (target_type, variant) = if variant_name == "ok" {
                (ok_type, "ok")
            } else {
                (err_type, "err")
            };
            return self.extract_value_from_ptr(data_ptr, target_type, variant);
        }
        Err(format!(
            "Type mismatch: expected Result, got {:?}",
            match_expr_type
        ))
    }

    fn bind_custom_enum_variant_args(
        &mut self,
        enum_name: &str,
        variant_name: &str,
        args: &[PatternNode],
        temp_ptr: inkwell::values::PointerValue<'a>,
    ) -> Result<(), String> {
        let struct_type = self
            .type_map
            .get(enum_name)
            .ok_or_else(|| format!("Enum {} not found in type map", enum_name))?
            .into_struct_type();

        let field_types_clone = self.variant_field_types(enum_name, variant_name)?;

        for (i, arg) in args.iter().enumerate() {
            if let PatternNode::Identifier(var) = arg {
                let data_index = i + 1;
                let data_ptr = self
                    .builder
                    .build_struct_gep(struct_type, temp_ptr, data_index as u32, "data_ptr")
                    .map_err(|e| e.to_string())?;

                let field_type: BasicTypeEnum<'_> =
                    self.variant_field_llvm_type(enum_name, variant_name, &field_types_clone, i)?;

                let data_val = self
                    .builder
                    .build_load(field_type, data_ptr, "data")
                    .map_err(|e| e.to_string())?;
                let boxed = self.box_value(data_val);
                let ptr_type = self.context.ptr_type(AddressSpace::default());
                let alloca = self
                    .builder
                    .build_alloca(ptr_type, var)
                    .map_err(|e| e.to_string())?;
                self.builder
                    .build_store(alloca, boxed)
                    .map_err(|e| e.to_string())?;

                let resolved_type = self.variant_field_resolved_type(
                    enum_name,
                    variant_name,
                    &field_types_clone,
                    i,
                )?;

                self.variables
                    .insert(var.clone(), (alloca, ptr_type.into(), resolved_type));
            }
        }

        Ok(())
    }

    fn variant_field_types(
        &self,
        enum_name: &str,
        variant_name: &str,
    ) -> Result<Vec<EnumVariantField>, String> {
        let variant_fields = self
            .enum_variant_fields
            .get(enum_name)
            .ok_or_else(|| format!("No field information found for enum {}", enum_name))?;
        variant_fields
            .get(variant_name)
            .cloned()
            .ok_or_else(|| format!("Variant {} not found in enum {}", variant_name, enum_name))
    }

    fn variant_field_llvm_type(
        &self,
        enum_name: &str,
        variant_name: &str,
        field_types: &[EnumVariantField],
        index: usize,
    ) -> Result<BasicTypeEnum<'a>, String> {
        if index >= field_types.len() {
            return Err(format!(
                "Field index {} out of bounds for enum variant {}.{} (has {} fields)",
                index,
                enum_name,
                variant_name,
                field_types.len()
            ));
        }
        self.type_kind_to_llvm_type(&field_types[index].1.kind)
    }

    fn variant_field_resolved_type(
        &mut self,
        enum_name: &str,
        variant_name: &str,
        field_types: &[EnumVariantField],
        index: usize,
    ) -> Result<Type, String> {
        if index >= field_types.len() {
            return Err(format!(
                "Field index {} out of bounds for enum variant {}.{} during type resolution (has {} fields)",
                index,
                enum_name,
                variant_name,
                field_types.len()
            ));
        }
        self.analyzer
            .resolve_type(&field_types[index].1)
            .map_err(|e| e.to_string())
    }

    fn bind_enum_arm_variables(
        &mut self,
        arm: &crate::ast::MatchArm,
        enum_name: &str,
        match_expr_type: &Type,
        expr_ptr_opt: Option<inkwell::values::PointerValue<'a>>,
        temp_ptr_opt: Option<inkwell::values::PointerValue<'a>>,
    ) -> Result<(), String> {
        let PatternNode::EnumVariant { name, args } = &arm.pattern else {
            return Ok(());
        };

        if let Some(expr_ptr) = expr_ptr_opt {
            return self.bind_builtin_enum_variant_args(
                enum_name,
                name,
                args,
                expr_ptr,
                match_expr_type,
            );
        }

        let temp_ptr = temp_ptr_opt.ok_or_else(|| "Temp pointer should be Some".to_string())?;
        self.bind_custom_enum_variant_args(enum_name, name, args, temp_ptr)
    }

    /// Generate match code for enum types using discriminant-based comparison.
    fn generate_enum_match(
        &mut self,
        function: &FunctionValue<'a>,
        match_expr: &ExpressionNode,
        match_expr_type: &Type,
        expr_val: BasicValueEnum<'a>,
        arms: &[crate::ast::MatchArm],
    ) -> Result<(), String> {
        let enum_name = self.resolve_enum_match_name(match_expr).or_else(|_| {
            self.enum_name_from_type(match_expr_type, "Match expression must be an enum type")
        })?;
        let expr_ptr_opt = self.enum_expr_ptr_for_payload_access(&enum_name, expr_val)?;

        let discriminant = self.load_enum_discriminant(&enum_name, expr_val)?;
        let temp_ptr_opt = self.enum_temp_struct_ptr(&enum_name, expr_val)?;

        let mut current_bb = self
            .builder
            .get_insert_block()
            .expect("Builder should have an insertion block");
        let all_arms_return = arms.iter().all(|arm| {
            arm.body
                .last()
                .is_some_and(|s| matches!(s.kind, StatementKind::Return(_)))
        });
        let match_id = self.label_counter;
        self.label_counter += 1;
        let end_bb = self
            .context
            .append_basic_block(*function, &format!("match_end_{}", match_id));

        for (i, arm) in arms.iter().enumerate() {
            let arm_bb = self
                .context
                .append_basic_block(*function, &format!("match_arm_{}_{}", match_id, i));
            let next_bb = if i < arms.len() - 1 {
                self.context
                    .append_basic_block(*function, &format!("match_next_{}_{}", match_id, i))
            } else {
                end_bb
            };

            self.builder.position_at_end(current_bb);
            if current_bb.get_terminator().is_some() {
                current_bb = next_bb;
                continue;
            }

            let pattern_matches =
                self.enum_pattern_matches(&enum_name, discriminant, &arm.pattern)?;

            self.builder
                .build_conditional_branch(pattern_matches, arm_bb, next_bb)
                .map_err(|e| e.to_string())?;

            self.builder.position_at_end(arm_bb);

            self.bind_enum_arm_variables(
                arm,
                &enum_name,
                match_expr_type,
                expr_ptr_opt,
                temp_ptr_opt,
            )?;

            self.emit_match_guard_and_body(function, arm, next_bb, end_bb, i)?;

            current_bb = next_bb;
        }

        self.builder.position_at_end(end_bb);
        if all_arms_return && end_bb.get_terminator().is_none() {
            self.builder
                .build_unreachable()
                .map_err(|e| e.to_string())?;
        }
        Ok(())
    }

    /// Generate match code for non-enum types using equality-based comparison.
    fn generate_switch_match(
        &mut self,
        function: &FunctionValue<'a>,
        match_expr_type: &Type,
        match_val: BasicValueEnum<'a>,
        arms: &[crate::ast::MatchArm],
    ) -> Result<(), String> {
        let mut current_bb = self
            .builder
            .get_insert_block()
            .expect("Builder should have an insertion block");
        let all_arms_return = arms.iter().all(|arm| {
            arm.body
                .last()
                .is_some_and(|s| matches!(s.kind, StatementKind::Return(_)))
        });
        let match_id = self.label_counter;
        self.label_counter += 1;
        let end_bb = self
            .context
            .append_basic_block(*function, &format!("match_end_{}", match_id));

        for (i, arm) in arms.iter().enumerate() {
            let arm_bb = self
                .context
                .append_basic_block(*function, &format!("match_arm_{}_{}", match_id, i));
            let next_bb = if i < arms.len() - 1 {
                self.context
                    .append_basic_block(*function, &format!("match_next_{}_{}", match_id, i))
            } else {
                end_bb
            };

            self.builder.position_at_end(current_bb);
            if current_bb.get_terminator().is_some() {
                current_bb = next_bb;
                continue;
            }

            let condition =
                self.evaluate_switch_pattern_condition(match_val, match_expr_type, &arm.pattern)?;

            self.builder
                .build_conditional_branch(condition, arm_bb, next_bb)
                .map_err(|e| e.to_string())?;

            self.builder.position_at_end(arm_bb);

            // For list patterns, bind variables after the condition check succeeds
            if let PatternNode::List { elements, rest } = &arm.pattern {
                self.bind_list_pattern_variables(
                    match_val,
                    match_expr_type,
                    elements,
                    rest.as_deref(),
                )?;
            }

            self.emit_match_guard_and_body(function, arm, next_bb, end_bb, i)?;

            current_bb = next_bb;
        }

        self.builder.position_at_end(end_bb);
        if all_arms_return && end_bb.get_terminator().is_none() {
            self.builder
                .build_unreachable()
                .map_err(|e| e.to_string())?;
        }
        Ok(())
    }

    /// Call a runtime function that returns an i32 and convert the result to an
    /// i1 bool (non-zero = true).
    fn call_runtime_bool(
        &mut self,
        left: inkwell::values::PointerValue<'a>,
        right: inkwell::values::PointerValue<'a>,
        func_name: &str,
        label: &str,
    ) -> Result<inkwell::values::IntValue<'a>, String> {
        let func = self
            .runtime_function(func_name)
            .ok_or_else(|| format!("{} not found", func_name))?;
        let result = self
            .builder
            .build_call(func, &[left.into(), right.into()], label)
            .map_err(|e| e.to_string())?
            .try_as_basic_value()
            .basic()
            .ok_or_else(|| format!("{} returned no value", func_name))?;
        let int_val = match result {
            BasicValueEnum::IntValue(v) => v,
            _ => return Err(format!("runtime function {} must return i32", func_name)),
        };
        self.i32_to_bool(int_val).map(|v| v.into_int_value())
    }

    /// Generate an equality comparison between two values based on their type.
    fn generate_value_equality(
        &mut self,
        left: BasicValueEnum<'a>,
        right: BasicValueEnum<'a>,
        expr_type: &Type,
    ) -> Result<inkwell::values::IntValue<'a>, String> {
        match expr_type {
            Type::Primitive(PrimitiveType::Int) | Type::Primitive(PrimitiveType::Char) => {
                let left_raw = self.get_raw_int_value(left)?;
                let right_raw = self.get_raw_int_value(right)?;
                self.builder
                    .build_int_compare(inkwell::IntPredicate::EQ, left_raw, right_raw, "eq")
                    .map_err(|e| e.to_string())
            }
            Type::Primitive(PrimitiveType::Bool) => {
                let left_raw = self.get_raw_bool_value(left)?;
                let right_raw = self.get_raw_bool_value(right)?;
                self.builder
                    .build_int_compare(inkwell::IntPredicate::EQ, left_raw, right_raw, "eq")
                    .map_err(|e| e.to_string())
            }
            Type::Primitive(PrimitiveType::Float) => {
                let left_float = self.get_raw_float_value(left)?;
                let right_float = self.get_raw_float_value(right)?;
                self.builder
                    .build_float_compare(
                        inkwell::FloatPredicate::OEQ,
                        left_float,
                        right_float,
                        "feq",
                    )
                    .map_err(|e| e.to_string())
            }
            Type::Primitive(PrimitiveType::Str) => {
                let left_ptr = self.ensure_pointer(left);
                let right_ptr = self.ensure_pointer(right);
                let left_cstr = self.extract_c_string_from_value(left_ptr)?;
                let right_cstr = self.extract_c_string_from_value(right_ptr)?;
                self.call_runtime_bool(left_cstr, right_cstr, "mux_string_equal", "string_equal")
            }
            Type::List(_)
            | Type::Map(_, _)
            | Type::Set(_)
            | Type::Tuple(_, _)
            | Type::EmptyList
            | Type::EmptyMap
            | Type::EmptySet
            | Type::EmptySetOrMap => {
                let left_ptr = self.ensure_pointer(left);
                let right_ptr = self.ensure_pointer(right);
                self.call_runtime_bool(left_ptr, right_ptr, "mux_value_equal", "value_equal")
            }
            _ => Err(format!(
                "Equality comparison not supported for match type: {:?}",
                expr_type
            )),
        }
    }

    /// Generate check for a list structural pattern.
    /// Returns an i1 condition that is true if the pattern matches.
    fn generate_list_pattern_check(
        &mut self,
        match_val: BasicValueEnum<'a>,
        match_expr_type: &Type,
        elements: &[PatternNode],
        rest: Option<&PatternNode>,
    ) -> Result<inkwell::values::IntValue<'a>, String> {
        let val_ptr = if match_val.is_pointer_value() {
            match_val.into_pointer_value()
        } else {
            self.box_value(match_val)
        };

        let len_fn = self
            .runtime_function("mux_value_list_length")
            .ok_or("mux_value_list_length not found")?;
        let len_result = self
            .builder
            .build_call(len_fn, &[val_ptr.into()], "list_len")
            .map_err(|e| e.to_string())?
            .try_as_basic_value()
            .basic()
            .ok_or("mux_value_list_length returned no value")?;
        let list_len = len_result.into_int_value();
        let required_len = self
            .context
            .i64_type()
            .const_int(elements.len() as u64, false);

        let len_check = if rest.is_some() {
            self.builder
                .build_int_compare(inkwell::IntPredicate::SGE, list_len, required_len, "len_ge")
                .map_err(|e| e.to_string())?
        } else {
            self.builder
                .build_int_compare(inkwell::IntPredicate::EQ, list_len, required_len, "len_eq")
                .map_err(|e| e.to_string())?
        };

        if elements.is_empty() && rest.is_none() {
            return Ok(len_check);
        }

        let inner_type = match match_expr_type {
            Type::List(inner) => (**inner).clone(),
            _ => return Ok(len_check),
        };

        let get_fn = self
            .runtime_function("mux_value_list_get_value")
            .ok_or("mux_value_list_get_value not found")?;

        let mut combined = len_check;
        for (i, elem) in elements.iter().enumerate() {
            if let PatternNode::Literal(lit) = elem {
                let idx = self.context.i64_type().const_int(i as u64, false);
                let elem_result = self
                    .builder
                    .build_call(get_fn, &[val_ptr.into(), idx.into()], "list_elem")
                    .map_err(|e| e.to_string())?
                    .try_as_basic_value()
                    .basic()
                    .ok_or("mux_value_list_get_value returned no value")?;

                let pattern_val = self.generate_literal(lit)?;
                let elem_eq =
                    self.generate_value_equality(elem_result, pattern_val, &inner_type)?;
                combined = self
                    .builder
                    .build_and(combined, elem_eq, "and_check")
                    .map_err(|e| e.to_string())?;
            }
        }

        Ok(combined)
    }

    /// Bind variables for list pattern elements after condition check succeeds.
    fn bind_list_pattern_variables(
        &mut self,
        match_val: BasicValueEnum<'a>,
        match_expr_type: &Type,
        elements: &[PatternNode],
        rest: Option<&PatternNode>,
    ) -> Result<(), String> {
        let inner_type = match match_expr_type {
            Type::List(inner) => (**inner).clone(),
            Type::EmptyList => return Ok(()),
            _ => return Ok(()),
        };

        let val_ptr = if match_val.is_pointer_value() {
            match_val.into_pointer_value()
        } else {
            self.box_value(match_val)
        };

        let get_fn = self
            .runtime_function("mux_value_list_get_value")
            .ok_or("mux_value_list_get_value not found")?;

        let ptr_type = self.context.ptr_type(AddressSpace::default());

        for (i, elem) in elements.iter().enumerate() {
            if let PatternNode::Identifier(var) = elem {
                let idx = self.context.i64_type().const_int(i as u64, false);
                let elem_result = self
                    .builder
                    .build_call(get_fn, &[val_ptr.into(), idx.into()], "list_elem")
                    .map_err(|e| e.to_string())?
                    .try_as_basic_value()
                    .basic()
                    .ok_or("mux_value_list_get_value returned no value")?;

                let alloca = self
                    .builder
                    .build_alloca(ptr_type, var)
                    .map_err(|e| e.to_string())?;
                let elem_ptr = elem_result.into_pointer_value();
                self.builder
                    .build_store(alloca, elem_ptr)
                    .map_err(|e| e.to_string())?;
                self.variables
                    .insert(var.clone(), (alloca, ptr_type.into(), inner_type.clone()));
            }
        }

        if let Some(PatternNode::Identifier(rest_var)) = rest {
            let start_idx = self
                .context
                .i64_type()
                .const_int(elements.len() as u64, false);

            let len_fn = self
                .runtime_function("mux_value_list_length")
                .ok_or("mux_value_list_length not found")?;
            let len_result = self
                .builder
                .build_call(len_fn, &[val_ptr.into()], "list_len")
                .map_err(|e| e.to_string())?
                .try_as_basic_value()
                .basic()
                .ok_or("mux_value_list_length returned no value")?;
            let end_idx = len_result.into_int_value();

            let slice_fn = self
                .runtime_function("mux_value_list_slice")
                .ok_or("mux_value_list_slice not found")?;
            let rest_result = self
                .builder
                .build_call(
                    slice_fn,
                    &[val_ptr.into(), start_idx.into(), end_idx.into()],
                    "rest_list",
                )
                .map_err(|e| e.to_string())?
                .try_as_basic_value()
                .basic()
                .ok_or("mux_value_list_slice returned no value")?;

            let alloca = self
                .builder
                .build_alloca(ptr_type, rest_var)
                .map_err(|e| e.to_string())?;
            let rest_ptr = rest_result.into_pointer_value();
            self.builder
                .build_store(alloca, rest_ptr)
                .map_err(|e| e.to_string())?;
            let rest_type = Type::List(Box::new(inner_type));
            self.variables
                .insert(rest_var.clone(), (alloca, ptr_type.into(), rest_type));
        }

        Ok(())
    }
}
