use crate::filesystem::fsnotify::{self, FsEvent};
use crate::filesystem::vfs::mount::{MountFS, MountFSInode};
use crate::filesystem::vfs::permission::check_inode_permission;
use crate::filesystem::vfs::permission::PermissionMask;
use crate::filesystem::vfs::syscall::AtFlags;
use crate::filesystem::vfs::utils::rsplit_path;
use crate::filesystem::vfs::utils::user_path_at;
use crate::filesystem::vfs::FileType;
use crate::filesystem::vfs::IndexNode;
use crate::filesystem::vfs::InodeFlags;
use crate::filesystem::vfs::InodeMode;
use crate::filesystem::vfs::Metadata;
use crate::filesystem::vfs::SystemError;
use crate::filesystem::vfs::VFS_MAX_FOLLOW_SYMLINK_TIMES;
use crate::libs::casting::DowncastArc;
use crate::process::cred::CAPFlags;
use crate::process::ProcessManager;
use alloc::sync::Arc;

/// **创建硬连接的系统调用**
///    
/// ## 参数
///
/// - 'oldfd': 用于解析源文件路径的文件描述符
/// - 'old': 源文件路径
/// - 'newfd': 用于解析新文件路径的文件描述符
/// - 'new': 新文件将创建的路径
/// - 'flags': 标志位，仅以位或方式包含AT_EMPTY_PATH和AT_SYMLINK_FOLLOW
///
///
pub fn do_linkat(
    oldfd: i32,
    old: &str,
    newfd: i32,
    new: &str,
    flags: AtFlags,
) -> Result<usize, SystemError> {
    // new 路径不能为空（只有 old 支持 AT_EMPTY_PATH）
    if new.is_empty() || new.ends_with('/') {
        return Err(SystemError::ENOENT);
    }

    // flag包含其他未规定值时返回EINVAL
    if !(AtFlags::AT_EMPTY_PATH | AtFlags::AT_SYMLINK_FOLLOW).contains(flags) {
        return Err(SystemError::EINVAL);
    }

    // AT_EMPTY_PATH 需要调用者具有 CAP_DAC_READ_SEARCH 能力
    // 参考 Linux: "To use null names we require CAP_DAC_READ_SEARCH"
    if flags.contains(AtFlags::AT_EMPTY_PATH) {
        let pcb = ProcessManager::current_pcb();
        if !pcb.cred().has_capability(CAPFlags::CAP_DAC_READ_SEARCH) {
            return Err(SystemError::ENOENT);
        }
    }

    let follow_last_symlink = flags.contains(AtFlags::AT_SYMLINK_FOLLOW);
    let pcb = ProcessManager::current_pcb();

    // 得到源路径的inode
    let old_inode: Arc<dyn IndexNode> = if old.is_empty() {
        if flags.contains(AtFlags::AT_EMPTY_PATH) {
            // 在AT_EMPTY_PATH启用时，old可以为空，old_inode实际为oldfd所指文件，但该文件不能为目录。
            let binding = pcb.fd_table();
            let fd_table_guard = binding.read();
            let file = fd_table_guard
                .get_file_by_fd(oldfd)
                .ok_or(SystemError::EBADF)?;
            let old_inode = file.inode();
            old_inode
        } else {
            return Err(SystemError::ENOENT);
        }
    } else {
        let (old_begin_inode, old_remain_path) = user_path_at(&pcb, oldfd, old)?;
        old_begin_inode.lookup_follow_symlink2(
            &old_remain_path,
            VFS_MAX_FOLLOW_SYMLINK_TIMES,
            follow_last_symlink,
        )?
    };

    // 得到新创建节点的父节点
    let (new_begin_inode, new_remain_path) = user_path_at(&pcb, newfd, new)?;
    let (new_name, new_parent_path) = rsplit_path(&new_remain_path);
    let new_parent = new_begin_inode
        .lookup_follow_symlink(new_parent_path.unwrap_or("/"), VFS_MAX_FOLLOW_SYMLINK_TIMES)?;

    let new_parent_metadata = new_parent.metadata()?;
    if new_parent_metadata.file_type != FileType::Dir {
        return Err(SystemError::ENOTDIR);
    }

    // Match Linux may_create() error ordering: search permission is needed to
    // inspect the target, an existing target wins over a missing write bit,
    // and write permission is checked only for a negative target.
    check_inode_permission(&new_parent, &new_parent_metadata, PermissionMask::MAY_EXEC)?;
    match new_parent.find(new_name) {
        Ok(_) => return Err(SystemError::EEXIST),
        Err(SystemError::ENOENT) => {}
        Err(err) => return Err(err),
    }

    // Linux filename_create() lets an existing target win over a read-only
    // mount, but returns EROFS for a negative target before later EXDEV and
    // inode permission checks.
    let new_parent_fs = new_parent.fs();
    if let Some(mount_fs) = new_parent_fs.clone().downcast_arc::<MountFS>() {
        if mount_fs.is_readonly() {
            return Err(SystemError::EROFS);
        }
    }

    // 跨文件系统检查：类似 Linux 的 "if (dir->i_sb != inode->i_sb) return -EXDEV;"
    // 通过比较文件系统指针地址判断是否为同一文件系统实例
    let old_inode_fs = old_inode.fs();
    if !Arc::ptr_eq(&new_parent_fs, &old_inode_fs) {
        return Err(SystemError::EXDEV);
    }

    // Linux do_linkat() checks protected-hardlink policy after preparing the
    // negative target and checking the target mount identity.
    may_linkat(&old_inode)?;
    check_inode_permission(&new_parent, &new_parent_metadata, PermissionMask::MAY_WRITE)?;

    let old_metadata = old_inode.metadata()?;
    if old_metadata
        .flags
        .intersects(InodeFlags::S_APPEND | InodeFlags::S_IMMUTABLE)
    {
        return Err(SystemError::EPERM);
    }
    if old_metadata.file_type == FileType::Dir {
        return Err(SystemError::EPERM);
    }

    let notify = || {
        // Linux reports the source inode's changed link count before the
        // destination directory entry becomes visible to fsnotify consumers.
        fsnotify::fsnotify(FsEvent::ATTRIB, None, Some(&old_inode), 0);
        // fsnotify: the parent directory receives IN_CREATE (the hard-link target is not a directory, so IN_ISDIR is not set).
        fsnotify::fsnotify(
            FsEvent::CREATE,
            Some((&new_parent, new_name)),
            Some(&old_inode),
            0,
        );
    };
    if let Some(mounted) = new_parent.clone().downcast_arc::<MountFSInode>() {
        mounted.link_with_post_commit(new_name, &old_inode, notify)?;
    } else {
        new_parent.link(new_name, &old_inode)?;
        notify();
    }
    Ok(0)
}

/// 检查是否允许创建硬链接（对应Linux的may_linkat）
///
/// 当protected_hardlinks启用时，阻止以下情况的硬链接创建：
/// - 源文件不是普通文件（特殊文件如FIFO、设备等）
/// - 源文件有setuid位
/// - 源文件有可执行的setgid位
/// - 调用者对源文件没有读写权限
///
/// 除非调用者是文件所有者或拥有CAP_FOWNER能力
fn may_linkat(old_inode: &Arc<dyn IndexNode>) -> Result<(), SystemError> {
    let metadata = old_inode.metadata()?;
    let cred = ProcessManager::current_pcb().cred();

    // TODO: 检查sysctl_protected_hardlinks是否启用
    // 目前假设始终启用（更安全的默认值）
    // 未来可添加sysctl支持：
    // if !sysctl_protected_hardlinks() {
    //     return Ok(());
    // }

    // 文件所有者可以创建硬链接
    if cred.is_owner(&metadata) {
        return Ok(());
    }

    // 拥有CAP_FOWNER能力可以创建硬链接
    if cred.has_capability(CAPFlags::CAP_FOWNER) {
        return Ok(());
    }

    // 检查是否是"安全的"硬链接源
    if !safe_hardlink_source(old_inode, &metadata)? {
        return Err(SystemError::EPERM);
    }

    Ok(())
}

/// 判断硬链接源是否"安全"
///
/// 这是 protected_hardlinks 安全机制的核心检查函数。
/// 为了防止通过硬链接攻击获取提升的权限，只有满足所有安全条件的文件才允许被硬链接。
///
/// 返回 false 如果满足以下任一条件：
/// - inode 不是普通文件（特殊文件如 FIFO、设备、符号链接等）
/// - inode 有 setuid 位
/// - inode 有 setgid 位且 group-exec 位（可执行的 setgid 文件）
/// - 调用者对文件没有读写权限
///
/// 否则返回 true。
///
/// ## 参数
///
/// - `metadata`: 源 inode 的元数据
/// - `cred`: 当前进程的凭证
///
/// ## 返回值
///
/// - `Ok(true)`: 硬链接源安全，允许创建硬链接
/// - `Ok(false)`: 硬链接源不安全，不允许创建硬链接
/// - `Err(SystemError::EACCES)`: 权限检查失败
fn safe_hardlink_source(
    old_inode: &Arc<dyn IndexNode>,
    metadata: &Metadata,
) -> Result<bool, SystemError> {
    let mode = metadata.mode;
    let file_type = metadata.file_type;

    // 1. 必须是普通文件
    if file_type != FileType::File {
        return Ok(false);
    }

    // 2. 不能有setuid位
    if mode.contains(InodeMode::S_ISUID) {
        return Ok(false);
    }

    // 3. 不能是可执行的setgid文件
    if mode.contains(InodeMode::S_ISGID) && mode.contains(InodeMode::S_IXGRP) {
        return Ok(false);
    }

    // 4. 调用者必须有读写权限（对 Remote 权限模型的 FS，该检查会被绕过）
    if crate::filesystem::vfs::permission::check_inode_permission(
        old_inode,
        metadata,
        PermissionMask::MAY_READ | PermissionMask::MAY_WRITE,
    )
    .is_err()
    {
        return Ok(false);
    }

    Ok(true)
}
