use core::{
    ffi::{c_char, c_int, c_void},
    mem::MaybeUninit,
};

use yggdrasil_rt::io::{RawFd, SeekFrom};

use crate::{
    error::{CFdResult, CIntZeroResult, CIsizeResult, COffsetResult, ResultExt, TryFromExt},
    headers::{
        errno::Errno,
        sys_types::{off_t, pid_t},
    },
    io::{self, raw::RawFile, IntoRawFd, Read, ReadAt, Seek, Write, WriteAt},
    util::PointerExt,
};

#[no_mangle]
unsafe extern "C" fn close(fd: c_int) -> CIntZeroResult {
    let mut file = RawFile::e_try_from(fd)?;
    file.close()?;
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn dup(oldfd: c_int) -> CFdResult {
    let oldfile = RawFile::e_try_from(oldfd)?;
    let newfd = oldfile.duplicate(None)?.into_raw_fd();
    CFdResult::success(newfd)
}

#[no_mangle]
unsafe extern "C" fn dup2(oldfd: c_int, newfd: c_int) -> CFdResult {
    let oldfile = RawFile::e_try_from(oldfd)?;
    let newfd = RawFd::e_try_from(newfd)?;
    let newfd = oldfile.duplicate(Some(newfd))?.into_raw_fd();
    CFdResult::success(newfd)
}

#[no_mangle]
unsafe extern "C" fn fdatasync(fd: c_int) -> CIntZeroResult {
    log::error!("TODO: fdatasync()");
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn fsync(fd: c_int) -> CIntZeroResult {
    log::error!("TODO: fsync()");
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn isatty(fd: c_int) -> c_int {
    log::error!("TODO: isatty()");
    0
}

#[no_mangle]
unsafe extern "C" fn lseek(fd: c_int, offset: off_t, whence: c_int) -> COffsetResult {
    let mut file = RawFile::e_try_from(fd)?;
    let seek = SeekFrom::e_try_from((offset, whence))?;
    let pos = file.seek(seek)?;
    COffsetResult::success(pos)
}

#[no_mangle]
unsafe extern "C" fn pipe(fds: *mut c_int) -> CIntZeroResult {
    let fds = core::slice::from_raw_parts_mut(fds, 2);
    let (read, write) = io::create_pipe(false, false)?;
    fds[0] = read.into_raw().try_into().unwrap();
    fds[1] = write.into_raw().try_into().unwrap();
    CIntZeroResult::SUCCESS
}

#[no_mangle]
unsafe extern "C" fn pread(fd: c_int, buf: *mut c_void, len: usize, pos: off_t) -> CIsizeResult {
    let mut file = RawFile::e_try_from(fd)?;
    let pos = u64::try_from(pos).e_map_err(|_| Errno::EINVAL)?;
    let data = buf.cast::<u8>().ensure_slice_mut(len);
    let count = file.read_at(pos, data)?;
    CIsizeResult::success(count)
}

#[no_mangle]
unsafe extern "C" fn pwrite(fd: c_int, buf: *const c_void, len: usize, pos: off_t) -> CIsizeResult {
    let mut file = RawFile::e_try_from(fd)?;
    let pos = u64::try_from(pos).e_map_err(|_| Errno::EINVAL)?;
    let data = buf.cast::<u8>().ensure_slice(len);
    let count = file.write_at(pos, data)?;
    CIsizeResult::success(count)
}

#[no_mangle]
unsafe extern "C" fn sync() {
    log::error!("TODO: sync()");
}

#[no_mangle]
unsafe extern "C" fn tcgetpgrp(fd: c_int) -> pid_t {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> CIntZeroResult {
    todo!()
}

#[no_mangle]
unsafe extern "C" fn write(fd: c_int, buf: *const c_void, count: usize) -> CIsizeResult {
    let mut file = RawFile::e_try_from(fd)?;
    let data = core::slice::from_raw_parts(buf.cast::<u8>(), count);
    let count = file.write(data)?;

    CIsizeResult::success(count)
}

#[no_mangle]
unsafe extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> CIsizeResult {
    let mut file = RawFile::e_try_from(fd)?;
    let data = core::slice::from_raw_parts_mut(buf.cast::<u8>(), count);
    let count = file.read(data)?;
    CIsizeResult::success(count)
}
