// Runtime function and type index definitions for the WASM backend.
//
// This module is the single source of truth for:
// - runtime function indices
// - canonical WASM function signatures used by runtime helpers and host imports
// - mapping runtime functions back to their type-section entries

use wasm_encoder::{TypeSection, ValType};

/// Index assignments for runtime functions within the module.
#[derive(Debug, Clone, Copy)]
pub struct RuntimeFuncIndices {
    pub alloc: u32,
    pub truncate: u32,        // (i32) -> ()
    pub collect_begin: u32,   // (i32) -> ()
    pub collect_end: u32,     // () -> ()
    pub rebase_i32: u32,      // (i32) -> i32
    pub retain_i32: u32,      // (i32) -> i32
    pub wrap: u32,            // (i32, i64, i32) -> i32
    pub wrap_f64: u32,        // (i32, f64) -> i32
    pub wrap_i32: u32,        // (i32, i32, i32) -> i32
    pub unwrap: u32,          // (i32) -> i64
    pub unwrap_f64: u32,      // (i32) -> f64
    pub unwrap_i32: u32,      // (i32) -> i32
    pub obj_kind: u32,        // (i32) -> i32
    pub obj_tag: u32,         // (i32) -> i32
    pub obj_meta: u32,        // (i32) -> i32
    pub obj_field: u32,       // (i32, i32) -> i64
    pub obj_field_f64: u32,   // (i32, i32) -> f64
    pub obj_field_i32: u32,   // (i32, i32) -> i32
    pub list_cons: u32,       // (i64, i32, i32) -> i32
    pub list_cons_f64: u32,   // (f64, i32) -> i32
    pub int_to_str: u32,      // (i64, i32) -> i32
    pub float_to_str: u32,    // (f64, i32) -> i32
    pub fd_write_buf: u32,    // (i32, i32) -> ()
    pub str_eq: u32,          // (i32, i32) -> i32
    pub str_concat: u32,      // (i32, i32) -> i32
    pub i64_to_str_obj: u32,  // (i64) -> i32
    pub f64_to_str_obj: u32,  // (f64) -> i32
    pub list_take: u32,       // (i32, i32) -> i32
    pub list_drop: u32,       // (i32, i32) -> i32
    pub list_concat: u32,     // (i32, i32) -> i32
    pub list_reverse: u32,    // (i32) -> i32
    pub list_contains: u32,   // (i32, i64) -> i32
    pub list_zip: u32,        // (i32, i32) -> i32
    pub map_get: u32,         // (i32, i32) -> i32
    pub map_set: u32,         // (i32, i32, i64, i32) -> i32
    pub map_has: u32,         // (i32, i32) -> i32
    pub map_keys: u32,        // (i32) -> i32
    pub map_entries: u32,     // (i32) -> i32
    pub vec_from_list: u32,   // (i32, i32) -> i32
    pub vec_get: u32,         // (i32, i64) -> i32
    pub vec_len: u32,         // (i32) -> i64
    pub vec_set: u32,         // (i32, i64, i64) -> i32
    pub vec_new: u32,         // (i64, i64, i32) -> i32
    pub vec_to_list: u32,     // (i32) -> i32
    pub str_len: u32,         // (i32) -> i64
    pub str_byte_len: u32,    // (i32) -> i64
    pub str_find: u32,        // (i32, i32, i32) -> i32
    pub str_starts_with: u32, // (i32, i32) -> i32
    pub str_ends_with: u32,   // (i32, i32) -> i32
    pub str_contains: u32,    // (i32, i32) -> i32
    pub str_char_at: u32,     // (i32, i64) -> i32
    pub char_from_code: u32,  // (i64) -> i32
    pub char_to_code: u32,    // (i32) -> i64
    pub byte_to_hex: u32,     // (i64) -> i32
    pub byte_from_hex: u32,   // (i32) -> i32
    pub str_trim: u32,        // (i32) -> i32
    pub str_slice: u32,       // (i32, i32, i32) -> i32
    pub str_chars: u32,       // (i32) -> i32
    pub str_split: u32,       // (i32, i32) -> i32
    pub str_join: u32,        // (i32, i32) -> i32
    pub str_replace: u32,     // (i32, i32, i32) -> i32
    pub str_to_lower: u32,    // (i32) -> i32
    pub str_to_upper: u32,    // (i32) -> i32
    pub int_from_str: u32,    // (i32) -> i32
    pub float_from_str: u32,  // (i32) -> i32
    /// Total number of runtime functions.
    pub count: u32,
    /// Import function index for writing to stdout (either WASI fd_write or aver/console_print).
    pub fd_write_import: u32,
    /// Which adapter mode is active.
    pub adapter: super::super::WasmAdapter,
}

impl RuntimeFuncIndices {
    pub fn new(base: u32) -> Self {
        let mut i = base;
        let mut next = || {
            let idx = i;
            i += 1;
            idx
        };
        RuntimeFuncIndices {
            alloc: next(),
            truncate: next(),
            collect_begin: next(),
            collect_end: next(),
            rebase_i32: next(),
            retain_i32: next(),
            wrap: next(),
            wrap_f64: next(),
            wrap_i32: next(),
            unwrap: next(),
            unwrap_f64: next(),
            unwrap_i32: next(),
            obj_kind: next(),
            obj_tag: next(),
            obj_meta: next(),
            obj_field: next(),
            obj_field_f64: next(),
            obj_field_i32: next(),
            list_cons: next(),
            list_cons_f64: next(),
            int_to_str: next(),
            float_to_str: next(),
            fd_write_buf: next(),
            str_eq: next(),
            str_concat: next(),
            i64_to_str_obj: next(),
            f64_to_str_obj: next(),
            list_take: next(),
            list_drop: next(),
            list_concat: next(),
            list_reverse: next(),
            list_contains: next(),
            list_zip: next(),
            map_get: next(),
            map_set: next(),
            map_has: next(),
            map_keys: next(),
            map_entries: next(),
            vec_from_list: next(),
            vec_get: next(),
            vec_len: next(),
            vec_set: next(),
            vec_new: next(),
            vec_to_list: next(),
            str_len: next(),
            str_byte_len: next(),
            str_find: next(),
            str_starts_with: next(),
            str_ends_with: next(),
            str_contains: next(),
            str_char_at: next(),
            char_from_code: next(),
            char_to_code: next(),
            byte_to_hex: next(),
            byte_from_hex: next(),
            str_trim: next(),
            str_slice: next(),
            str_chars: next(),
            str_split: next(),
            str_join: next(),
            str_replace: next(),
            str_to_lower: next(),
            str_to_upper: next(),
            int_from_str: next(),
            float_from_str: next(),
            count: i - base,
            fd_write_import: 0,
            adapter: super::super::WasmAdapter::Aver,
        }
    }
}

#[derive(Default)]
struct TypeRegistry {
    entries: Vec<(Vec<ValType>, Vec<ValType>)>,
}

impl TypeRegistry {
    fn intern(
        &mut self,
        type_section: &mut TypeSection,
        params: &[ValType],
        results: &[ValType],
    ) -> u32 {
        if let Some((idx, _)) = self
            .entries
            .iter()
            .enumerate()
            .find(|(_, (ps, rs))| ps.as_slice() == params && rs.as_slice() == results)
        {
            return idx as u32;
        }

        let idx = self.entries.len() as u32;
        type_section
            .ty()
            .function(params.to_vec(), results.to_vec());
        self.entries.push((params.to_vec(), results.to_vec()));
        idx
    }

    fn count(&self) -> u32 {
        self.entries.len() as u32
    }
}

/// Runtime and import function type signatures. Indices into the type section.
#[derive(Debug, Clone, Copy)]
pub struct RtTypeIndices {
    pub alloc: u32,                  // (i32) -> i32
    pub i32_to_empty: u32,           // (i32) -> ()
    pub wrap_i64: u32,               // (i32, i64, i32) -> i32
    pub wrap_f64: u32,               // (i32, f64) -> i32
    pub wrap_i32: u32,               // (i32, i32, i32) -> i32
    pub unwrap_i64: u32,             // (i32) -> i64
    pub unwrap_f64: u32,             // (i32) -> f64
    pub unwrap_i32: u32,             // (i32) -> i32
    pub obj_kind: u32,               // (i32) -> i32
    pub obj_tag: u32,                // (i32) -> i32
    pub obj_meta: u32,               // (i32) -> i32
    pub obj_field_i64: u32,          // (i32, i32) -> i64
    pub obj_field_f64: u32,          // (i32, i32) -> f64
    pub obj_field_i32: u32,          // (i32, i32) -> i32
    pub list_cons_i64: u32,          // (i64, i32, i32) -> i32
    pub list_cons_f64: u32,          // (f64, i32) -> i32
    pub print_i64: u32,              // (i64) -> ()
    pub print_f64: u32,              // (f64) -> ()
    pub print_i32: u32,              // (i32) -> ()
    pub int_to_str: u32,             // (i64, i32) -> i32
    pub float_to_str: u32,           // (f64, i32) -> i32
    pub fd_write_buf: u32,           // (i32, i32) -> ()
    pub wasi_fd_write: u32,          // (i32, i32, i32, i32) -> i32
    pub i32_i32_to_i32: u32,         // (i32, i32) -> i32
    pub i64_i32_to_i32: u32,         // (i64, i32) -> i32
    pub i64_to_i32: u32,             // (i64) -> i32
    pub f64_to_i32: u32,             // (f64) -> i32
    pub i32_i64_to_i32: u32,         // (i32, i64) -> i32
    pub i32_i64_i32_to_i32: u32,     // (i32, i64, i32) -> i32
    pub i32_i32_i64_to_i32: u32,     // (i32, i32, i64) -> i32
    pub i32_i32_i64_i32_to_i32: u32, // (i32, i32, i64, i32) -> i32
    pub i32_i64_i64_to_i32: u32,     // (i32, i64, i64) -> i32
    pub i64_i64_to_i32: u32,         // (i64, i64) -> i32
    pub i64_i64_i32_to_i32: u32,     // (i64, i64, i32) -> i32
    pub f64_to_f64: u32,             // (f64) -> f64
    pub f64_f64_to_f64: u32,         // (f64, f64) -> f64
    pub i32_i32_i32_to_i32: u32,     // (i32, i32, i32) -> i32
    pub empty_to_i32: u32,           // () -> i32
    pub empty_to_i32_i32: u32,       // () -> (i32, i32)
    pub i32_to_i32_i32: u32,         // (i32) -> (i32, i32)
    pub i32_i64_to_empty: u32,       // (i32, i64) -> ()
    pub i32_i64_to_i32_i32: u32,     // (i32, i64) -> (i32, i32)
    pub i64_i64_to_i64: u32,         // (i64, i64) -> i64
    pub empty_to_i64: u32,           // () -> i64
    pub empty_to_empty: u32,         // () -> ()
    pub count: u32,                  // total number of distinct base signatures
}

/// Emit and intern all base signatures used by runtime helpers and ABI imports.
pub fn emit_base_type_section(type_section: &mut TypeSection) -> RtTypeIndices {
    let mut registry = TypeRegistry::default();

    let alloc = registry.intern(type_section, &[ValType::I32], &[ValType::I32]);
    let i32_to_empty = registry.intern(type_section, &[ValType::I32], &[]);
    let wrap_i64 = registry.intern(
        type_section,
        &[ValType::I32, ValType::I64, ValType::I32],
        &[ValType::I32],
    );
    let wrap_f64 = registry.intern(type_section, &[ValType::I32, ValType::F64], &[ValType::I32]);
    let wrap_i32 = registry.intern(
        type_section,
        &[ValType::I32, ValType::I32, ValType::I32],
        &[ValType::I32],
    );
    let unwrap_i64 = registry.intern(type_section, &[ValType::I32], &[ValType::I64]);
    let unwrap_f64 = registry.intern(type_section, &[ValType::I32], &[ValType::F64]);
    let unwrap_i32 = registry.intern(type_section, &[ValType::I32], &[ValType::I32]);
    let obj_field_i64 =
        registry.intern(type_section, &[ValType::I32, ValType::I32], &[ValType::I64]);
    let obj_field_f64 =
        registry.intern(type_section, &[ValType::I32, ValType::I32], &[ValType::F64]);
    let list_cons_i64 = registry.intern(
        type_section,
        &[ValType::I64, ValType::I32, ValType::I32],
        &[ValType::I32],
    );
    let list_cons_f64 =
        registry.intern(type_section, &[ValType::F64, ValType::I32], &[ValType::I32]);
    let print_i64 = registry.intern(type_section, &[ValType::I64], &[]);
    let print_f64 = registry.intern(type_section, &[ValType::F64], &[]);
    let print_i32 = registry.intern(type_section, &[ValType::I32], &[]);
    let fd_write_buf = registry.intern(type_section, &[ValType::I32, ValType::I32], &[]);
    let wasi_fd_write = registry.intern(
        type_section,
        &[ValType::I32, ValType::I32, ValType::I32, ValType::I32],
        &[ValType::I32],
    );
    let i32_i32_to_i32 =
        registry.intern(type_section, &[ValType::I32, ValType::I32], &[ValType::I32]);
    let i64_i32_to_i32 =
        registry.intern(type_section, &[ValType::I64, ValType::I32], &[ValType::I32]);
    let i64_to_i32 = registry.intern(type_section, &[ValType::I64], &[ValType::I32]);
    let f64_to_i32 = registry.intern(type_section, &[ValType::F64], &[ValType::I32]);
    let i32_i64_to_i32 =
        registry.intern(type_section, &[ValType::I32, ValType::I64], &[ValType::I32]);
    let i32_i64_i32_to_i32 = registry.intern(
        type_section,
        &[ValType::I32, ValType::I64, ValType::I32],
        &[ValType::I32],
    );
    let i32_i32_i64_to_i32 = registry.intern(
        type_section,
        &[ValType::I32, ValType::I32, ValType::I64],
        &[ValType::I32],
    );
    let i32_i32_i64_i32_to_i32 = registry.intern(
        type_section,
        &[ValType::I32, ValType::I32, ValType::I64, ValType::I32],
        &[ValType::I32],
    );
    let i32_i64_i64_to_i32 = registry.intern(
        type_section,
        &[ValType::I32, ValType::I64, ValType::I64],
        &[ValType::I32],
    );
    let i64_i64_to_i32 =
        registry.intern(type_section, &[ValType::I64, ValType::I64], &[ValType::I32]);
    let i64_i64_i32_to_i32 = registry.intern(
        type_section,
        &[ValType::I64, ValType::I64, ValType::I32],
        &[ValType::I32],
    );
    let f64_to_f64 = registry.intern(type_section, &[ValType::F64], &[ValType::F64]);
    let f64_f64_to_f64 =
        registry.intern(type_section, &[ValType::F64, ValType::F64], &[ValType::F64]);
    let i32_i32_i32_to_i32 = registry.intern(
        type_section,
        &[ValType::I32, ValType::I32, ValType::I32],
        &[ValType::I32],
    );
    let empty_to_i32 = registry.intern(type_section, &[], &[ValType::I32]);
    let empty_to_i32_i32 = registry.intern(type_section, &[], &[ValType::I32, ValType::I32]);
    let i32_to_i32_i32 =
        registry.intern(type_section, &[ValType::I32], &[ValType::I32, ValType::I32]);
    let i32_i64_to_empty = registry.intern(type_section, &[ValType::I32, ValType::I64], &[]);
    let i32_i64_to_i32_i32 = registry.intern(
        type_section,
        &[ValType::I32, ValType::I64],
        &[ValType::I32, ValType::I32],
    );
    let i64_i64_to_i64 =
        registry.intern(type_section, &[ValType::I64, ValType::I64], &[ValType::I64]);
    let empty_to_i64 = registry.intern(type_section, &[], &[ValType::I64]);
    let empty_to_empty = registry.intern(type_section, &[], &[]);

    RtTypeIndices {
        alloc,
        i32_to_empty,
        wrap_i64,
        wrap_f64,
        wrap_i32,
        unwrap_i64,
        unwrap_f64,
        unwrap_i32,
        obj_kind: unwrap_i32,
        obj_tag: unwrap_i32,
        obj_meta: unwrap_i32,
        obj_field_i64,
        obj_field_f64,
        obj_field_i32: i32_i32_to_i32,
        list_cons_i64,
        list_cons_f64,
        print_i64,
        print_f64,
        print_i32,
        int_to_str: i64_i32_to_i32,
        float_to_str: list_cons_f64,
        fd_write_buf,
        wasi_fd_write,
        i32_i32_to_i32,
        i64_i32_to_i32,
        i64_to_i32,
        f64_to_i32,
        i32_i64_to_i32,
        i32_i64_i32_to_i32,
        i32_i32_i64_to_i32,
        i32_i32_i64_i32_to_i32,
        i32_i64_i64_to_i32,
        i64_i64_to_i32,
        i64_i64_i32_to_i32,
        f64_to_f64,
        f64_f64_to_f64,
        i32_i32_i32_to_i32,
        empty_to_i32,
        empty_to_i32_i32,
        i32_to_i32_i32,
        i32_i64_to_empty,
        i32_i64_to_i32_i32,
        i64_i64_to_i64,
        empty_to_i64,
        empty_to_empty,
        count: registry.count(),
    }
}

/// Lookup the canonical type index for a function signature already interned in the base table.
pub fn lookup_type_index(
    rti: &RtTypeIndices,
    params: &[ValType],
    results: &[ValType],
) -> Option<u32> {
    if params == [ValType::I32] && results.is_empty() {
        return Some(rti.i32_to_empty);
    }
    if params == [ValType::I32] && results == [ValType::I32] {
        return Some(rti.unwrap_i32);
    }
    if params == [ValType::I32, ValType::I64, ValType::I32] && results == [ValType::I32] {
        return Some(rti.wrap_i64);
    }
    if params == [ValType::I32, ValType::F64] && results == [ValType::I32] {
        return Some(rti.wrap_f64);
    }
    if params == [ValType::I32, ValType::I32, ValType::I32] && results == [ValType::I32] {
        return Some(rti.wrap_i32);
    }
    if params == [ValType::I32] && results == [ValType::I64] {
        return Some(rti.unwrap_i64);
    }
    if params == [ValType::I32] && results == [ValType::F64] {
        return Some(rti.unwrap_f64);
    }
    if params == [ValType::I32, ValType::I32] && results == [ValType::I64] {
        return Some(rti.obj_field_i64);
    }
    if params == [ValType::I32, ValType::I32] && results == [ValType::F64] {
        return Some(rti.obj_field_f64);
    }
    if params == [ValType::I64, ValType::I32, ValType::I32] && results == [ValType::I32] {
        return Some(rti.list_cons_i64);
    }
    if params == [ValType::F64, ValType::I32] && results == [ValType::I32] {
        return Some(rti.list_cons_f64);
    }
    if params == [ValType::I64] && results.is_empty() {
        return Some(rti.print_i64);
    }
    if params == [ValType::F64] && results.is_empty() {
        return Some(rti.print_f64);
    }
    if params == [ValType::I32] && results.is_empty() {
        return Some(rti.print_i32);
    }
    if params == [ValType::I32, ValType::I32] && results.is_empty() {
        return Some(rti.fd_write_buf);
    }
    if params == [ValType::I32, ValType::I32, ValType::I32, ValType::I32]
        && results == [ValType::I32]
    {
        return Some(rti.wasi_fd_write);
    }
    if params == [ValType::I64, ValType::I32] && results == [ValType::I32] {
        return Some(rti.i64_i32_to_i32);
    }
    if params == [ValType::I64] && results == [ValType::I32] {
        return Some(rti.i64_to_i32);
    }
    if params == [ValType::F64] && results == [ValType::I32] {
        return Some(rti.f64_to_i32);
    }
    if params == [ValType::I32, ValType::I64] && results == [ValType::I32] {
        return Some(rti.i32_i64_to_i32);
    }
    if params == [ValType::I32, ValType::I64, ValType::I32] && results == [ValType::I32] {
        return Some(rti.i32_i64_i32_to_i32);
    }
    if params == [ValType::I32, ValType::I32, ValType::I64] && results == [ValType::I32] {
        return Some(rti.i32_i32_i64_to_i32);
    }
    if params == [ValType::I32, ValType::I32, ValType::I64, ValType::I32]
        && results == [ValType::I32]
    {
        return Some(rti.i32_i32_i64_i32_to_i32);
    }
    if params == [ValType::I32, ValType::I64, ValType::I64] && results == [ValType::I32] {
        return Some(rti.i32_i64_i64_to_i32);
    }
    if params == [ValType::I64, ValType::I64] && results == [ValType::I32] {
        return Some(rti.i64_i64_to_i32);
    }
    if params == [ValType::I64, ValType::I64, ValType::I32] && results == [ValType::I32] {
        return Some(rti.i64_i64_i32_to_i32);
    }
    if params == [ValType::F64] && results == [ValType::F64] {
        return Some(rti.f64_to_f64);
    }
    if params == [ValType::F64, ValType::F64] && results == [ValType::F64] {
        return Some(rti.f64_f64_to_f64);
    }
    if params == [ValType::I32, ValType::I32, ValType::I32] && results == [ValType::I32] {
        return Some(rti.i32_i32_i32_to_i32);
    }
    if params.is_empty() && results == [ValType::I32] {
        return Some(rti.empty_to_i32);
    }
    if params.is_empty() && results == [ValType::I32, ValType::I32] {
        return Some(rti.empty_to_i32_i32);
    }
    if params == [ValType::I32] && results == [ValType::I32, ValType::I32] {
        return Some(rti.i32_to_i32_i32);
    }
    if params == [ValType::I32, ValType::I64] && results.is_empty() {
        return Some(rti.i32_i64_to_empty);
    }
    if params == [ValType::I32, ValType::I64] && results == [ValType::I32, ValType::I32] {
        return Some(rti.i32_i64_to_i32_i32);
    }
    if params == [ValType::I64, ValType::I64] && results == [ValType::I64] {
        return Some(rti.i64_i64_to_i64);
    }
    if params.is_empty() && results == [ValType::I64] {
        return Some(rti.empty_to_i64);
    }
    if params.is_empty() && results.is_empty() {
        return Some(rti.empty_to_empty);
    }

    None
}

/// Get the type index for a given runtime function.
pub fn rt_type_index(
    rt: &RuntimeFuncIndices,
    rti: &RtTypeIndices,
    func_idx: u32,
    import_func_count: u32,
) -> u32 {
    let local_idx = func_idx - import_func_count;
    let alloc_local = rt.alloc - import_func_count;

    if local_idx == alloc_local {
        return rti.alloc;
    }
    if local_idx == rt.truncate - import_func_count {
        return rti.i32_to_empty;
    }
    if local_idx == rt.collect_begin - import_func_count {
        return rti.i32_to_empty;
    }
    if local_idx == rt.collect_end - import_func_count {
        return rti.empty_to_empty;
    }
    if local_idx == rt.rebase_i32 - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.retain_i32 - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.wrap - import_func_count {
        return rti.wrap_i64;
    }
    if local_idx == rt.wrap_f64 - import_func_count {
        return rti.wrap_f64;
    }
    if local_idx == rt.wrap_i32 - import_func_count {
        return rti.wrap_i32;
    }
    if local_idx == rt.unwrap - import_func_count {
        return rti.unwrap_i64;
    }
    if local_idx == rt.unwrap_f64 - import_func_count {
        return rti.unwrap_f64;
    }
    if local_idx == rt.unwrap_i32 - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.obj_kind - import_func_count {
        return rti.obj_kind;
    }
    if local_idx == rt.obj_tag - import_func_count {
        return rti.obj_tag;
    }
    if local_idx == rt.obj_meta - import_func_count {
        return rti.obj_meta;
    }
    if local_idx == rt.obj_field - import_func_count {
        return rti.obj_field_i64;
    }
    if local_idx == rt.obj_field_f64 - import_func_count {
        return rti.obj_field_f64;
    }
    if local_idx == rt.obj_field_i32 - import_func_count {
        return rti.obj_field_i32;
    }
    if local_idx == rt.list_cons - import_func_count {
        return rti.list_cons_i64;
    }
    if local_idx == rt.list_cons_f64 - import_func_count {
        return rti.list_cons_f64;
    }
    if local_idx == rt.int_to_str - import_func_count {
        return rti.int_to_str;
    }
    if local_idx == rt.float_to_str - import_func_count {
        return rti.float_to_str;
    }
    if local_idx == rt.fd_write_buf - import_func_count {
        return rti.fd_write_buf;
    }
    if local_idx == rt.str_eq - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.str_concat - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.i64_to_str_obj - import_func_count {
        return rti.i64_to_i32;
    }
    if local_idx == rt.f64_to_str_obj - import_func_count {
        return rti.f64_to_i32;
    }
    if local_idx == rt.list_take - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.list_drop - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.list_concat - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.list_reverse - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.list_contains - import_func_count {
        return rti.i32_i64_to_i32;
    }
    if local_idx == rt.list_zip - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.map_get - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.map_set - import_func_count {
        return rti.i32_i32_i64_i32_to_i32;
    }
    if local_idx == rt.map_has - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.map_keys - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.map_entries - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.vec_from_list - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.vec_get - import_func_count {
        return rti.i32_i64_to_i32;
    }
    if local_idx == rt.vec_len - import_func_count {
        return rti.unwrap_i64;
    }
    if local_idx == rt.vec_set - import_func_count {
        return rti.i32_i64_i64_to_i32;
    }
    if local_idx == rt.vec_new - import_func_count {
        return rti.i64_i64_i32_to_i32;
    }
    if local_idx == rt.vec_to_list - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.str_len - import_func_count {
        return rti.unwrap_i64;
    }
    if local_idx == rt.str_byte_len - import_func_count {
        return rti.unwrap_i64;
    }
    if local_idx == rt.str_find - import_func_count {
        return rti.i32_i32_i32_to_i32;
    }
    if local_idx == rt.str_starts_with - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.str_ends_with - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.str_contains - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.str_char_at - import_func_count {
        return rti.i32_i64_to_i32;
    }
    if local_idx == rt.char_from_code - import_func_count {
        return rti.i64_to_i32;
    }
    if local_idx == rt.char_to_code - import_func_count {
        return rti.unwrap_i64;
    }
    if local_idx == rt.byte_to_hex - import_func_count {
        return rti.i64_to_i32;
    }
    if local_idx == rt.byte_from_hex - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.str_trim - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.str_slice - import_func_count {
        return rti.i32_i32_i32_to_i32;
    }
    if local_idx == rt.str_chars - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.str_split - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.str_join - import_func_count {
        return rti.i32_i32_to_i32;
    }
    if local_idx == rt.str_replace - import_func_count {
        return rti.i32_i32_i32_to_i32;
    }
    if local_idx == rt.str_to_lower - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.str_to_upper - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.int_from_str - import_func_count {
        return rti.unwrap_i32;
    }
    if local_idx == rt.float_from_str - import_func_count {
        return rti.unwrap_i32;
    }

    panic!(
        "Unknown runtime function index: {} (base={})",
        func_idx, import_func_count
    );
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn base_type_section_interns_shared_signatures() {
        let mut type_section = TypeSection::new();
        let rti = emit_base_type_section(&mut type_section);

        assert_eq!(rti.alloc, rti.unwrap_i32);
        assert_eq!(rti.obj_kind, rti.unwrap_i32);
        assert_eq!(rti.obj_tag, rti.unwrap_i32);
        assert_eq!(rti.obj_meta, rti.unwrap_i32);
        assert_eq!(rti.list_cons_f64, rti.float_to_str);
    }

    #[test]
    fn abi_lookup_covers_effect_signatures() {
        let mut type_section = TypeSection::new();
        let rti = emit_base_type_section(&mut type_section);

        assert_eq!(
            lookup_type_index(&rti, &[ValType::I64, ValType::I64], &[ValType::I64]),
            Some(rti.i64_i64_to_i64)
        );
        assert_eq!(
            lookup_type_index(&rti, &[], &[ValType::I64]),
            Some(rti.empty_to_i64)
        );
        assert_eq!(
            lookup_type_index(
                &rti,
                &[ValType::I32, ValType::I64],
                &[ValType::I32, ValType::I32]
            ),
            Some(rti.i32_i64_to_i32_i32)
        );
    }
}
