use crate::fs::fops::FileOps;
use crate::fs::open_file::OpenFile;
use crate::process::thread_group::pid::PidT;
use crate::process::{Tid, find_task_by_tid};
use crate::sched::syscall_ctx::ProcessCtx;
use alloc::boxed::Box;
use alloc::sync::Arc;
use async_trait::async_trait;
use bitflags::bitflags;
use libkernel::error::{KernelError, Result};
use libkernel::fs::OpenFlags;
use libkernel::memory::address::UA;

bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    pub struct PidfdFlags: u32 {
        const PIDFD_NONBLOCK = OpenFlags::O_NONBLOCK.bits();
        const PIDFD_THREAD = OpenFlags::O_EXCL.bits();
    }
}

pub struct PidFile {
    _pid: Tid,
    _flags: PidfdFlags,
}

impl PidFile {
    pub fn new(pid: Tid, flags: PidfdFlags) -> Self {
        Self {
            _pid: pid,
            _flags: flags,
        }
    }

    pub fn new_open_file(pid: Tid, flags: PidfdFlags) -> Arc<OpenFile> {
        let file = PidFile::new(pid, flags);
        Arc::new(OpenFile::new(
            Box::new(file),
            OpenFlags::from_bits(flags.bits()).unwrap(),
        ))
    }
}

#[async_trait]
impl FileOps for PidFile {
    async fn readat(&mut self, _buf: UA, _count: usize, _offset: u64) -> Result<usize> {
        Err(KernelError::InvalidValue)
    }

    async fn writeat(&mut self, _buf: UA, _count: usize, _offset: u64) -> Result<usize> {
        Err(KernelError::InvalidValue)
    }
}

pub async fn sys_pidfd_open(ctx: &ProcessCtx, pid: PidT, flags: u32) -> Result<usize> {
    let pid = Tid::from_pid_t(pid);
    let flags = PidfdFlags::from_bits(flags).ok_or(KernelError::InvalidValue)?;
    if !flags.contains(PidfdFlags::PIDFD_THREAD) {
        // Ensure the pid exists and is a thread group leader.
        let _ = find_task_by_tid(pid).unwrap();
    }

    let file = PidFile::new_open_file(pid, flags);

    let fd = ctx.task().fd_table.lock_save_irq().insert(file)?;

    Ok(fd.as_raw() as _)
}
