// Copyright (C) 2026 Ordinary Labs, LLC.
//
// SPDX-License-Identifier: AGPL-3.0-only

use anyhow::bail;
use wasi_common::WasiCtx;
use wasmtime::{AsContext, AsContextMut, Caller, Extern};

pub fn read_mem(caller: &mut Caller<'_, WasiCtx>, ptr: i32, buf: &mut [u8]) -> anyhow::Result<()> {
    let Some(Extern::Memory(mem)) = caller.get_export("memory") else {
        bail!("no mem");
    };

    mem.read(caller.as_context_mut(), usize::try_from(ptr)?, buf)?;
    Ok(())
}

pub fn write_mem(caller: &mut Caller<'_, WasiCtx>, bytes: &[u8]) -> Option<i64> {
    let alloc = match caller.get_export("alloc") {
        Some(Extern::Func(malloc)) => match malloc.typed::<i32, i32>(caller.as_context()) {
            Ok(malloc) => malloc,
            Err(_) => return None,
        },
        _ => return None,
    };

    let len = bytes.len();

    let Ok(params) = i32::try_from(len) else {
        return None;
    };

    let Ok(ptr) = alloc.call(caller.as_context_mut(), params) else {
        return None;
    };

    let Some(Extern::Memory(mem)) = caller.get_export("memory") else {
        return None;
    };

    let Ok(usize_ptr) = usize::try_from(ptr) else {
        return None;
    };

    if mem
        .write(caller.as_context_mut(), usize_ptr, bytes)
        .is_err()
    {
        return None;
    }

    let i64_ptr = i64::from(ptr) << 32;

    let Ok(i64_len) = i64::try_from(len) else {
        return None;
    };

    Some(i64_ptr | i64_len)
}
