//! /proc/[pid]/fd - 进程文件描述符目录
//!
//! 这个目录包含了进程打开的所有文件描述符的符号链接

use crate::filesystem::{
    procfs::{
        pid::ProcPidTarget,
        template::{Builder, DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps},
    },
    vfs::{IndexNode, InodeMode, SpecialNodeData},
};
use crate::libs::casting::DowncastArc;
use alloc::{
    format,
    string::ToString,
    sync::{Arc, Weak},
    vec::Vec,
};
use system_error::SystemError;

/// /proc/[pid]/fd 目录的 DirOps 实现
#[derive(Debug)]
pub struct FdDirOps {
    target: ProcPidTarget,
}

impl FdDirOps {
    pub fn new_inode(target: ProcPidTarget, parent: Weak<dyn IndexNode>) -> Arc<dyn IndexNode> {
        ProcDirBuilder::new(Self { target }, InodeMode::from_bits_truncate(0o500)) // dr-x------
            .parent(parent)
            .volatile() // fd 是易失的，因为它们与特定进程关联
            .build()
            .unwrap()
    }

    fn get_process(&self) -> Option<Arc<crate::process::ProcessControlBlock>> {
        self.target.thread_group_leader()
    }
}

impl DirOps for FdDirOps {
    fn lookup_child(
        &self,
        dir: &ProcDir<Self>,
        name: &str,
    ) -> Result<Arc<dyn IndexNode>, SystemError> {
        // 解析文件描述符编号
        let fd = name.parse::<i32>().map_err(|_| SystemError::ENOENT)?;

        // 获取进程引用
        let process = self.get_process().ok_or(SystemError::ENOENT)?;

        // 检查文件描述符是否存在，并立即释放fd_table锁
        {
            // A process can become a zombie after ProcPidTarget resolved it.
            // exit_files() has already removed its descriptor table in that
            // state, so /proc/<pid>/fd must report a disappearing entry rather
            // than calling ProcessControlBlock::fd_table(), which unwraps.
            let fd_table = process
                .basic()
                .try_fd_table()
                .clone()
                .ok_or(SystemError::ENOENT)?;
            let fd_table_guard = fd_table.read();

            if fd_table_guard.get_file_by_fd(fd).is_none() {
                return Err(SystemError::ENOENT);
            }
        } // fd_table_guard在这里被释放

        // 创建或获取缓存的符号链接
        let mut cached_children = dir.cached_children().write();

        if let Some(child) = cached_children.get(name) {
            return Ok(child.clone());
        }

        // 创建新的符号链接（传递 PID 和 fd）
        let inode = FdSymOps::new_inode(self.target.clone(), fd, dir.self_ref_weak().clone());
        cached_children.insert(name.to_string(), inode.clone());

        Ok(inode)
    }

    fn populate_children(&self, dir: &ProcDir<Self>) {
        let mut cached_children = dir.cached_children().write();

        // 清空现有缓存
        cached_children.clear();

        // 获取进程的所有文件描述符
        if let Some(process) = self.get_process() {
            // 先收集所有的fd，避免在持有锁时做复杂操作
            let Some(fd_table) = process.basic().try_fd_table().clone() else {
                return;
            };
            let fds: Vec<i32> = {
                let fd_table_guard = fd_table.read();
                fd_table_guard.iter().map(|(fd, _)| fd).collect()
            };

            // 现在fd_table锁已经释放，我们可以安全地创建inodes
            for fd in fds {
                let fd_str = fd.to_string();

                // 创建或获取缓存的符号链接（传递 PID 和 fd）
                cached_children.entry(fd_str.clone()).or_insert_with(|| {
                    FdSymOps::new_inode(self.target.clone(), fd, dir.self_ref_weak().clone())
                });
            }
        }
        // 写锁在这里自动释放
    }

    fn validate_child(&self, child: &dyn IndexNode) -> bool {
        child.special_node().is_some()
    }
}

/// /proc/[pid]/fd/[fd] 符号链接的 SymOps 实现
#[derive(Debug)]
pub struct FdSymOps {
    target: ProcPidTarget,
    fd: i32,
}

impl FdSymOps {
    pub fn new_inode(
        target: ProcPidTarget,
        fd: i32,
        parent: Weak<dyn IndexNode>,
    ) -> Arc<dyn IndexNode> {
        ProcSymBuilder::new(Self { target, fd }, InodeMode::from_bits_truncate(0o700)) // lrwx------
            .parent(parent)
            .volatile()
            .build()
            .unwrap()
    }
}

impl SymOps for FdSymOps {
    fn read_link(&self, buf: &mut [u8]) -> Result<usize, SystemError> {
        let process = self
            .target
            .thread_group_leader()
            .ok_or(SystemError::ENOENT)?;

        // 先获取文件对象的 clone，然后立即释放 fd_table 锁
        // 避免在持有锁时调用可能获取其他锁的方法（如 absolute_path）
        let file = {
            let fd_table = process
                .basic()
                .try_fd_table()
                .clone()
                .ok_or(SystemError::ENOENT)?;
            let fd_table_guard = fd_table.read();

            fd_table_guard
                .get_file_by_fd(self.fd)
                .ok_or(SystemError::ENOENT)?
        }; // fd_table 锁在这里被释放

        // 获取进程的 chroot 根路径
        let root_prefix = process
            .try_fs_struct()
            .ok_or(SystemError::ENOENT)?
            .root()
            .absolute_path()
            .unwrap_or_default();

        // 现在安全地获取文件的路径
        let inode = file.path_inode();
        let path_result = if let Some(mount_inode) = inode
            .clone()
            .downcast_arc::<crate::filesystem::vfs::mount::MountFSInode>(
        ) {
            mount_inode.procfs_path()
        } else {
            inode.absolute_path()
        };
        let mut path = if let Ok(p) = path_result {
            p
        } else {
            // 匿名文件或无法获取路径
            let inode_id = inode.metadata()?.inode_id;
            format!("anon_inode:[{}]", inode_id)
        };

        // 尊重进程的 chroot：去掉根目录前缀
        if !root_prefix.is_empty() && root_prefix != "/" {
            if let Some(rest) = path.strip_prefix(&root_prefix) {
                path = if rest.is_empty() {
                    "/".to_string()
                } else if rest.starts_with('/') {
                    rest.to_string()
                } else {
                    format!("/{}", rest)
                };
            }
        }

        // 复制路径到缓冲区
        let path_bytes = path.as_bytes();
        let copy_len = path_bytes.len().min(buf.len());
        buf[..copy_len].copy_from_slice(&path_bytes[..copy_len]);

        Ok(copy_len)
    }

    fn special_node(&self) -> Option<SpecialNodeData> {
        let process = self.target.thread_group_leader()?;

        // 获取文件对象
        let file = {
            let fd_table = process.basic().try_fd_table().clone()?;
            let fd_table_guard = fd_table.read();
            fd_table_guard.get_file_by_fd(self.fd)?
        };

        // 返回文件的 inode 引用，使得 fstatat 等操作可以通过魔法链接工作
        Some(SpecialNodeData::Reference(file.path_inode()))
    }
}
