// String-related C ABI exports.

// String.len_h(handle) -> i64
#[export_name = "nyash.string.len_h"]
pub extern "C" fn nyash_string_len_h(handle: i64) -> i64 {
    use nyash_rust::runtime::host_handles as handles;
    if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() == Some("1") {
        let present = if handle > 0 {
            handles::get(handle as u64).is_some()
        } else {
            false
        };
        eprintln!(
            "[AOT-LEN_H] string.len_h handle={} present={}",
            handle, present
        );
    }
    if handle <= 0 {
        return 0;
    }
    if let Some(obj) = handles::get(handle as u64) {
        if let Some(sb) = obj
            .as_any()
            .downcast_ref::<nyash_rust::box_trait::StringBox>()
        {
            return sb.value.len() as i64;
        }
    }
    0
}

// FAST-path helper: compute string length from raw pointer (i8*) with mode (reserved)
// Exported as both legacy name (nyash.string.length_si) and neutral name (nyrt_string_length)
#[export_name = "nyrt_string_length"]
pub extern "C" fn nyrt_string_length(ptr: *const i8, _mode: i64) -> i64 {
    use std::ffi::CStr;
    if ptr.is_null() {
        return 0;
    }
    // Safety: pointer is expected to point to a null-terminated UTF-8 byte string
    let c = unsafe { CStr::from_ptr(ptr) };
    match c.to_bytes().len() {
        n => n as i64,
    }
}

// String.charCodeAt_h(handle, idx) -> i64 (byte-based; -1 if OOB)
#[export_name = "nyash.string.charCodeAt_h"]
pub extern "C" fn nyash_string_charcode_at_h_export(handle: i64, idx: i64) -> i64 {
    use nyash_rust::runtime::host_handles as handles;
    if idx < 0 {
        return -1;
    }
    if handle <= 0 {
        return -1;
    }
    if let Some(obj) = handles::get(handle as u64) {
        if let Some(sb) = obj
            .as_any()
            .downcast_ref::<nyash_rust::box_trait::StringBox>()
        {
            let s = &sb.value;
            let i = idx as usize;
            if i < s.len() {
                return s.as_bytes()[i] as i64;
            }
            return -1;
        }
    }
    -1
}

// String.concat_hh(lhs_h, rhs_h) -> handle
#[export_name = "nyash.string.concat_hh"]
pub extern "C" fn nyash_string_concat_hh_export(a_h: i64, b_h: i64) -> i64 {
    use nyash_rust::{
        box_trait::{NyashBox, StringBox},
        runtime::host_handles as handles,
    };
    let to_s = |h: i64| -> String {
        if h > 0 {
            if let Some(o) = handles::get(h as u64) {
                return o.to_string_box().value;
            }
        }
        String::new()
    };
    let s = format!("{}{}", to_s(a_h), to_s(b_h));
    nyash_rust::runtime::global_hooks::gc_alloc(s.len() as u64);
    let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s));
    let h = handles::to_handle_arc(arc) as i64;
    h
}

// String.eq_hh(lhs_h, rhs_h) -> i64 (0/1)
#[export_name = "nyash.string.eq_hh"]
pub extern "C" fn nyash_string_eq_hh_export(a_h: i64, b_h: i64) -> i64 {
    use nyash_rust::runtime::host_handles as handles;
    let to_s = |h: i64| -> String {
        if h > 0 {
            if let Some(o) = handles::get(h as u64) {
                return o.to_string_box().value;
            }
        }
        String::new()
    };
    if to_s(a_h) == to_s(b_h) {
        1
    } else {
        0
    }
}

// String.substring_hii(handle, start, end) -> handle
#[export_name = "nyash.string.substring_hii"]
pub extern "C" fn nyash_string_substring_hii_export(h: i64, start: i64, end: i64) -> i64 {
    use nyash_rust::{box_trait::NyashBox, box_trait::StringBox, runtime::host_handles as handles};
    if h <= 0 {
        return 0;
    }
    let s = if let Some(obj) = handles::get(h as u64) {
        if let Some(sb) = obj.as_any().downcast_ref::<StringBox>() {
            sb.value.clone()
        } else {
            String::new()
        }
    } else {
        String::new()
    };
    let n = s.len() as i64;
    let mut st = if start < 0 { 0 } else { start };
    let mut en = if end < 0 { 0 } else { end };
    if st > n {
        st = n;
    }
    if en > n {
        en = n;
    }
    if en < st {
        std::mem::swap(&mut st, &mut en);
    }
    let (st_u, en_u) = (st as usize, en as usize);
    let sub = s.get(st_u.min(s.len())..en_u.min(s.len())).unwrap_or("");
    let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(sub.to_string()));
    nyash_rust::runtime::global_hooks::gc_alloc(sub.len() as u64);
    let nh = handles::to_handle_arc(arc) as i64;
    eprintln!("[TRACE] substring_hii -> {}", nh);
    nh
}

// String.lastIndexOf_hh(haystack_h, needle_h) -> i64
#[export_name = "nyash.string.lastIndexOf_hh"]
pub extern "C" fn nyash_string_lastindexof_hh_export(h: i64, n: i64) -> i64 {
    use nyash_rust::{box_trait::StringBox, runtime::host_handles as handles};
    let hay = if h > 0 {
        if let Some(o) = handles::get(h as u64) {
            if let Some(sb) = o.as_any().downcast_ref::<StringBox>() {
                sb.value.clone()
            } else {
                String::new()
            }
        } else {
            String::new()
        }
    } else {
        String::new()
    };
    let nee = if n > 0 {
        if let Some(o) = handles::get(n as u64) {
            if let Some(sb) = o.as_any().downcast_ref::<StringBox>() {
                sb.value.clone()
            } else {
                String::new()
            }
        } else {
            String::new()
        }
    } else {
        String::new()
    };
    if nee.is_empty() {
        return hay.len() as i64;
    }
    if let Some(pos) = hay.rfind(&nee) {
        pos as i64
    } else {
        -1
    }
}

// String.lt_hh(lhs_h, rhs_h) -> i64 (0/1)
#[export_name = "nyash.string.lt_hh"]
pub extern "C" fn nyash_string_lt_hh_export(a_h: i64, b_h: i64) -> i64 {
    use nyash_rust::runtime::host_handles as handles;
    let to_s = |h: i64| -> String {
        if h > 0 {
            if let Some(o) = handles::get(h as u64) {
                return o.to_string_box().value;
            }
        }
        String::new()
    };
    if to_s(a_h) < to_s(b_h) {
        1
    } else {
        0
    }
}

// Construct StringBox from two u64 words (little-endian) + length (<=16) and return handle
// export: nyash.string.from_u64x2(lo, hi, len) -> i64
#[export_name = "nyash.string.from_u64x2"]
pub extern "C" fn nyash_string_from_u64x2_export(lo: i64, hi: i64, len: i64) -> i64 {
    use nyash_rust::{
        box_trait::{NyashBox, StringBox},
        runtime::host_handles as handles,
    };
    let l = if len < 0 {
        0
    } else {
        core::cmp::min(len as usize, 16)
    };
    let mut bytes: Vec<u8> = Vec::with_capacity(l);
    let lo_u = lo as u64;
    let hi_u = hi as u64;
    for i in 0..l.min(8) {
        bytes.push(((lo_u >> (8 * i)) & 0xff) as u8);
    }
    for i in 0..l.saturating_sub(8) {
        bytes.push(((hi_u >> (8 * i)) & 0xff) as u8);
    }
    let s = String::from_utf8_lossy(&bytes).to_string();
    let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(StringBox::new(s.clone()));
    nyash_rust::runtime::global_hooks::gc_alloc(s.len() as u64);
    handles::to_handle_arc(arc) as i64
}
