#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "zvfs_hook_fcntl.h"
#include "zvfs_hook_init.h"
#include "zvfs_hook_fd.h"          /* dup/dup2 路径 */
#include "zvfs_hook_reentrant.h"
#include "fs/zvfs.h"
#include "fs/zvfs_open_file.h"
#include "fs/zvfs_inode.h"

#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <stdint.h>

/* ------------------------------------------------------------------ */
/* 内部：fcntl 核心逻辑（已确认是 zvfs fd）                             */
/* ------------------------------------------------------------------ */

static int
zvfs_fcntl_impl(int fd, int cmd, va_list ap)
{
    pthread_mutex_lock(&g_fs.fd_mu);
    struct zvfs_open_file *of = openfile_lookup(fd);
    pthread_mutex_unlock(&g_fs.fd_mu);

    if (!of) { errno = EBADF; return -1; }

    switch (cmd) {

    /* ---- 文件状态 flags ------------------------------------------ */
    case F_GETFL:
        return of->flags;

    case F_SETFL: {
        int newfl = va_arg(ap, int);
        /*
         * 只允许修改可变位：O_APPEND、O_NONBLOCK、O_ASYNC。
         * O_RDONLY / O_WRONLY / O_RDWR 是 open 时决定的，不能改。
         * 同步给真实 fd，保持内核状态一致（影响 real_read/write）。
         */
        int mutable_mask = O_APPEND | O_NONBLOCK | O_ASYNC;
        of->flags = (of->flags & ~mutable_mask) | (newfl & mutable_mask);
        /*
         * 也透传给真实 fd——虽然真实 fd 上的读写被我们拦截了，
         * 但 O_NONBLOCK 可能影响 pipe / socket 等透传路径。
         */
        real_fcntl(fd, F_SETFL, of->flags);
        return 0;
    }

    /* ---- fd flags（FD_CLOEXEC）----------------------------------- */
    case F_GETFD:
        return of->fd_flags;

    case F_SETFD: {
        int fdfl = va_arg(ap, int);
        of->fd_flags = fdfl;
        /* 同步给真实 fd */
        real_fcntl(fd, F_SETFD, fdfl);
        return 0;
    }

    /* ---- dup 类 -------------------------------------------------- */
    case F_DUPFD:
    case F_DUPFD_CLOEXEC: {
        int minfd = va_arg(ap, int);
        int newfd = real_fcntl(fd, cmd, minfd);
        if (newfd < 0)
            return -1;

        int new_fd_flags = (cmd == F_DUPFD_CLOEXEC) ? FD_CLOEXEC : 0;
        if (zvfs_dup_attach_newfd(fd, newfd, new_fd_flags) < 0) {
            int saved = errno;
            real_close(newfd);
            errno = saved;
            return -1;
        }
        return newfd;
    }

    /* ---- 文件锁（不实现，假装无锁）-------------------------------- */
    case F_GETLK: {
        struct flock *fl = va_arg(ap, struct flock *);
        if (!fl) { errno = EFAULT; return -1; }
        fl->l_type = F_UNLCK;   /* 假装没有任何锁 */
        return 0;
    }

    case F_SETLK:
    case F_SETLKW:
        (void)va_arg(ap, struct flock *);
        return 0;   /* 假装加锁成功 */

    /* ---- 其他 cmd：透传给内核（同时维护真实 fd 状态）-------------- */
    default: {
        /*
         * 取出可变参数作为 void* 透传。
         * 大多数 fcntl cmd 的第三个参数是 long 或指针，
         * 用 void* 接收足够覆盖所有平台（64-bit）。
         */
        void *arg = va_arg(ap, void *);
        return real_fcntl(fd, cmd, arg);
    }

    } /* switch */
}

/* ------------------------------------------------------------------ */
/* fcntl                                                                */
/* ------------------------------------------------------------------ */

int
fcntl(int fd, int cmd, ...)
{
    ZVFS_HOOK_ENTER();

    va_list ap;
    va_start(ap, cmd);

    int ret;
    if (ZVFS_IN_HOOK() || !zvfs_is_zvfs_fd(fd)) {
        /*
         * 非 zvfs fd：透传。
         * va_list 转发需要用 vfprintf 风格，但 fcntl 没有标准的
         * va_list 版本。用 void* 提取第三参数再透传。
         */
        void *arg = va_arg(ap, void *);
        ret = real_fcntl(fd, cmd, arg);
        va_end(ap);
        ZVFS_HOOK_LEAVE();
        return ret;
    }

    zvfs_ensure_init();

    ret = zvfs_fcntl_impl(fd, cmd, ap);
    va_end(ap);
    ZVFS_HOOK_LEAVE();
    return ret;
}

int
fcntl64(int fd, int cmd, ...)
{
    /*
     * fcntl64 是 glibc 在 32-bit 系统上的 large-file 变体，
     * 语义与 fcntl 相同，直接转发。
     */
    va_list ap;
    va_start(ap, cmd);
    void *arg = va_arg(ap, void *);
    va_end(ap);

    ZVFS_HOOK_ENTER();
    int ret;
    if (ZVFS_IN_HOOK() || !zvfs_is_zvfs_fd(fd)) {
        ret = real_fcntl64 ? real_fcntl64(fd, cmd, arg)
                           : real_fcntl(fd, cmd, arg);
        ZVFS_HOOK_LEAVE();
        return ret;
    }

    zvfs_ensure_init();

    va_list ap2;
    va_start(ap2, cmd);
    ret = zvfs_fcntl_impl(fd, cmd, ap2);
    va_end(ap2);

    ZVFS_HOOK_LEAVE();
    return ret;
}

/* ------------------------------------------------------------------ */
/* ioctl                                                                */
/* ------------------------------------------------------------------ */

int
ioctl(int fd, unsigned long request, ...)
{
    ZVFS_HOOK_ENTER();

    va_list ap;
    va_start(ap, request);
    void *arg = va_arg(ap, void *);
    va_end(ap);

    if (ZVFS_IN_HOOK() || !zvfs_is_zvfs_fd(fd)) {
        int ret = real_ioctl(fd, request, arg);
        ZVFS_HOOK_LEAVE();
        return ret;
    }

    zvfs_ensure_init();

    int ret = -1;

    switch (request) {

    case FIONREAD: {
        /*
         * 返回当前可读字节数 = logical_size - cur_offset。
         * 结果写入 arg（int*）。
         */
        pthread_mutex_lock(&g_fs.fd_mu);
        struct zvfs_open_file *of = openfile_lookup(fd);
        pthread_mutex_unlock(&g_fs.fd_mu);

        if (!of) { errno = EBADF; ret = -1; break; }

        pthread_mutex_lock(&of->inode->mu);
        uint64_t size = of->inode->logical_size;
        pthread_mutex_unlock(&of->inode->mu);

        uint64_t off    = of->offset;
        int      avail  = (off < size) ? (int)(size - off) : 0;
        if (arg) *(int *)arg = avail;
        ret = 0;
        break;
    }

    default:
        /*
         * 其他 ioctl：zvfs 文件不是块设备/字符设备，
         * 绝大多数 ioctl 语义不适用，返回 ENOTTY。
         * 若将来需要支持特定 ioctl 在此扩展。
         */
        errno = ENOTTY;
        ret = -1;
        break;
    }

    ZVFS_HOOK_LEAVE();
    return ret;
}
