use core::{
    ffi::{c_char, c_int},
    ptr::NonNull,
};

use crate::{
    env,
    error::{CIntZeroResult, CPtrResult, CResult, OptionExt},
    headers::errno::Errno,
    process,
    util::PointerStrExt,
};

#[no_mangle]
unsafe extern "C" fn _Exit(status: c_int) -> ! {
    process::c_exit_immediately(status)
}

#[no_mangle]
unsafe extern "C" fn abort() -> ! {
    process::abort()
}

#[no_mangle]
unsafe extern "C" fn atexit(f: extern "C" fn()) -> CIntZeroResult {
    process::at_exit(f);
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn exit(status: c_int) -> ! {
    process::c_exit(status)
}

#[no_mangle]
unsafe extern "C" fn getenv(name: *const c_char) -> CPtrResult<c_char> {
    let name = name.ensure_cstr().to_bytes();

    if let Some(value) = env::get_env(name)? {
        CPtrResult::success(value)
    } else {
        CPtrResult::ERROR
    }
}

#[no_mangle]
unsafe extern "C" fn putenv(value: *mut c_char) -> CIntZeroResult {
    let value = NonNull::new(value).e_ok_or(Errno::EINVAL)?;
    env::put_env(value)?;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn setenv(
    name: *const c_char,
    value: *const c_char,
    overwrite: c_int,
) -> CIntZeroResult {
    let name = name.ensure_cstr().to_bytes();
    let value = NonNull::new(value.cast_mut()).e_ok_or(Errno::EINVAL)?;
    let overwrite = overwrite != 0;
    env::set_env(name, value, overwrite)?;
    CIntZeroResult::SUCCESS
}

// Deprecated
#[no_mangle]
unsafe extern "C" fn setkey(_key: *const c_char) {
    unimplemented!()
}

#[no_mangle]
unsafe extern "C" fn system(command: *const c_char) -> CIntZeroResult {
    let command = command.ensure_str();
    log::warn!("system({command:?})");
    CIntZeroResult::ERROR
}

#[no_mangle]
unsafe extern "C" fn unsetenv(name: *const c_char) -> CIntZeroResult {
    let name = name.ensure_cstr().to_bytes();
    env::remove_env(name)?;
    CIntZeroResult::SUCCESS
}
