use std::ffi::{CStr, CString};
use std::os::raw::c_char;

// Symbols every module must export with `extern "C"` and `#[no_mangle]`.
// Signature: fn module_name() -> *const c_char
// Signature: fn module_invoke(op: *const c_char, payload: *const c_char) -> *mut c_char
pub const SYMBOL_NAME: &str = "module_name";
pub const SYMBOL_INVOKE: &str = "module_invoke";

// Helper to convert C char* to &str
pub unsafe fn cstr_to_str<'a>(ptr: *const c_char) -> anyhow::Result<&'a str> {
    if ptr.is_null() {
        anyhow::bail!("null pointer");
    }
    Ok(CStr::from_ptr(ptr).to_str()?)
}

// Helper to allocate a CString for return across FFI boundary (module side)
pub fn string_to_cstring_ptr(s: String) -> *mut c_char {
    CString::new(s).unwrap().into_raw()
}

// Helper to take back ownership of a CString (host side), then free by letting CString drop
pub unsafe fn take_cstring(ptr: *mut c_char) -> anyhow::Result<String> {
    if ptr.is_null() {
        anyhow::bail!("null pointer");
    }
    let s = CString::from_raw(ptr);
    Ok(s.into_string()?)
}
