use core::mem::MaybeUninit;

pub(crate) use abi::{
    debug::TraceLevel,
    error::Error,
    io::{
        AccessMode, DirectoryEntry, FileAttr, FileMode, FileSync, MountOptions, OpenOptions,
        PollControl, RawFd, RemoveFlags, TerminalOptions, TerminalSize, TimerOptions,
        UnmountOptions,
    },
    mem::{MappingFlags, MappingSource},
    net::{SocketShutdown, SocketType},
    process::{Signal, SignalEntryData, SpawnOptions, WaitFlags},
};
use abi::{
    option::OptionValue,
    path::Path,
    process::{ExecveOptions, ProcessId},
    system::{self, SystemControlVariant, SystemInfoVariant},
    time::{ClockType, SystemTime},
};
use libk::{
    module, random,
    task::thread::Thread,
    time::{monotonic_time, real_time, set_real_seconds},
};
use libk_mm::phys;

use crate::{
    arch::{Platform, PLATFORM},
    fs,
};

use super::run_with_io;

pub(super) mod sys_debug;
pub(super) mod sys_io;
pub(super) mod sys_net;
pub(super) mod sys_process;

pub(super) use sys_debug::*;
pub(super) use sys_io::*;
pub(super) use sys_net::*;
pub(super) use sys_process::*;

// Misc
pub(crate) fn get_random(buffer: &mut [u8]) {
    random::read_unsecure(buffer);
}

pub(crate) fn mount(options: &MountOptions<'_>) -> Result<(), Error> {
    let thread = Thread::current();
    let process = thread.process();

    run_with_io(&process, |mut io| {
        fs::mount_filesystem(io.ioctx_mut(), options)
    })
}

pub(crate) fn unmount(_options: &UnmountOptions) -> Result<(), Error> {
    todo!()
}

pub(crate) fn get_clock(ty: ClockType, value: &mut MaybeUninit<SystemTime>) -> Result<(), Error> {
    let time = match ty {
        ClockType::RealTime => real_time(),
        ClockType::Monotonic => monotonic_time(),
    };
    value.write(time);
    Ok(())
}

pub(crate) fn set_clock(ty: ClockType, value: &SystemTime) -> Result<(), Error> {
    match ty {
        ClockType::RealTime => {
            let time = chrono::DateTime::<chrono::Utc>::from_timestamp(
                value.seconds() as i64,
                value.subsec_nanos() as u32,
            );
            if let Some(time) = time {
                log::info!("Set real time from userspace: {time}");
            } else {
                log::info!("Set real time from userspace: {value:?}");
            };
            set_real_seconds(value.seconds(), true);
            Ok(())
        }
        ClockType::Monotonic => Err(Error::ReadOnly),
    }
}

pub(crate) fn system_control(option: u32, value: &mut [u8], size: usize) -> Result<usize, Error> {
    let _ = (value, size);
    let option = SystemControlVariant::try_from(option)?;
    match option {
        SystemControlVariant::PowerOff => {
            unsafe { PLATFORM.power_off() }?;
        }
    }
}

pub(crate) fn get_system_info(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
    let option = SystemInfoVariant::try_from(option)?;
    match option {
        SystemInfoVariant::MemoryUsage => system::MemoryUsage::store(&phys::stats(), buffer),
    }
}

pub(crate) fn load_module(path: &str) -> Result<(), Error> {
    let thread = Thread::current();
    let process = thread.process();

    let file = run_with_io(&process, |mut io| {
        let path: &Path = path.as_ref();
        if !path.is_absolute() {
            return Err(Error::InvalidArgument);
        }
        io.ioctx_mut().open_executable(path)
    })?;

    module::load_and_execute(file)
}

// Handled outside
pub(crate) fn exit_signal(_frame: &SignalEntryData) -> ! {
    unreachable!()
}

pub(crate) fn fork() -> Result<ProcessId, Error> {
    unreachable!()
}

pub(crate) fn execve(_options: &ExecveOptions<'_>) -> Result<(), Error> {
    unreachable!()
}
