#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "zvfs_hook_stat.h"
#include "zvfs_hook_init.h"
#include "zvfs_hook_reentrant.h"
#include "fs/zvfs.h"
#include "fs/zvfs_inode.h"
#include "fs/zvfs_open_file.h"
#include "fs/zvfs_path_entry.h"

#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdint.h>
#include <string.h>

/* ------------------------------------------------------------------ */
/* 内部：用 inode 覆盖 stat 结构体的 zvfs 相关字段                       */
/* ------------------------------------------------------------------ */

static void
patch_stat(struct stat *st, struct zvfs_inode *inode)
{
    pthread_mutex_lock(&inode->mu);
    st->st_size  = (off_t)inode->logical_size;
    st->st_atime = inode->atime;
    st->st_mtime = inode->mtime;
    pthread_mutex_unlock(&inode->mu);

    /*
     * st_blocks：以 512 字节为单位的"实际占用块数"。
     * zvfs 数据在 SPDK，真实文件几乎为空（只有 xattr），
     * 按 logical_size 估算，给上层一个合理的值。
     * (logical_size + 511) / 512 向上取整。
     */
    st->st_blocks = (blkcnt_t)((st->st_size + 511) / 512);
}

static void
patch_stat64(struct stat64 *st, struct zvfs_inode *inode)
{
    pthread_mutex_lock(&inode->mu);
    st->st_size  = (off64_t)inode->logical_size;
    st->st_atime = inode->atime;
    st->st_mtime = inode->mtime;
    pthread_mutex_unlock(&inode->mu);

    st->st_blocks = (blkcnt_t)((st->st_size + 511) / 512);
}

/* ------------------------------------------------------------------ */
/* 内部：按路径找 inode（先查缓存，缓存未命中则检查 xattr）               */
/* ------------------------------------------------------------------ */

/*
 * zvfs_inode_by_path
 *
 * 返回路径对应的 inode 指针（不增加 ref_count，调用方只读使用）。
 * 若路径不是 zvfs 文件（无 xattr）返回 NULL。
 *
 * 注意：返回的指针仅在持有 path_mu / inode_mu 之外使用时有效，
 * 调用方需在使用期间持有 inode->mu 或确保文件未被 close。
 * 对 stat 路径（只读 logical_size/atime/mtime），
 * 短暂持有 inode->mu 即可，无需长期持有。
 */
static struct zvfs_inode *
zvfs_inode_by_path(const char *path)
{
    /* 1. 先查 path_cache */
    pthread_mutex_lock(&g_fs.path_mu);
    struct zvfs_path_entry *pe = path_cache_lookup(path);
    struct zvfs_inode *inode = pe ? pe->inode : NULL;
    pthread_mutex_unlock(&g_fs.path_mu);

    if (inode)
        return inode;

    /* 2. path_cache 未命中：检查 xattr 判断是否是 zvfs 文件 */
    uint64_t blob_id = 0;
    int tmp_fd = real_open(path, O_RDONLY);
    if (tmp_fd < 0)
        return NULL;

    int has_xattr = (zvfs_xattr_read_blob_id(tmp_fd, &blob_id) == 0);
    real_close(tmp_fd);

    if (!has_xattr)
        return NULL;

    /* 3. 查 inode_table（文件被另一个 fd 打开过） */
    pthread_mutex_lock(&g_fs.inode_mu);
    inode = inode_lookup(blob_id);
    pthread_mutex_unlock(&g_fs.inode_mu);

    return inode;   /* 可能仍为 NULL（从未打开过，纯 stat 调用） */
}

/* ------------------------------------------------------------------ */
/* stat                                                                 */
/* ------------------------------------------------------------------ */

int
stat(const char *path, struct stat *buf)
{
    ZVFS_HOOK_ENTER();

    if (ZVFS_IN_HOOK() || !zvfs_is_zvfs_path(path)) {
        int r = zvfs_real_stat(path, buf);
        ZVFS_HOOK_LEAVE();
        return r;
    }

    zvfs_ensure_init();

    /* 先透传，拿到完整 stat（mode、ino、dev、nlink 等） */
    if (zvfs_real_stat(path, buf) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    struct zvfs_inode *inode = zvfs_inode_by_path(path);
    if (inode)
        patch_stat(buf, inode);
    /*
     * inode 为 NULL：文件存在于 FS 但从未被 zvfs open，
     * 此时 st_size 来自真实文件（接近 0），
     * 这是合理的降级行为（文件尚未被写入 SPDK）。
     */

    ZVFS_HOOK_LEAVE();
    return 0;
}

int
stat64(const char *path, struct stat64 *buf)
{
    ZVFS_HOOK_ENTER();

    if (ZVFS_IN_HOOK() || !zvfs_is_zvfs_path(path)) {
        int r = zvfs_real_stat64(path, buf);
        ZVFS_HOOK_LEAVE();
        return r;
    }

    zvfs_ensure_init();

    if (zvfs_real_stat64(path, buf) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    struct zvfs_inode *inode = zvfs_inode_by_path(path);
    if (inode)
        patch_stat64(buf, inode);

    ZVFS_HOOK_LEAVE();
    return 0;
}

/* ------------------------------------------------------------------ */
/* fstat（最高频，pg 每次 read 前都调）                                  */
/* ------------------------------------------------------------------ */

int
fstat(int fd, struct stat *buf)
{
    ZVFS_HOOK_ENTER();

    /* 先透传：拿到 mode/ino/dev/nlink/blksize 等 */
    if (zvfs_real_fstat(fd, buf) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    if (ZVFS_IN_HOOK()) {
        ZVFS_HOOK_LEAVE();
        return 0;
    }

    pthread_mutex_lock(&g_fs.fd_mu);
    struct zvfs_open_file *of = openfile_lookup(fd);
    pthread_mutex_unlock(&g_fs.fd_mu);

    if (of) {
        zvfs_ensure_init();
        patch_stat(buf, of->inode);
    }

    ZVFS_HOOK_LEAVE();
    return 0;
}

int
fstat64(int fd, struct stat64 *buf)
{
    ZVFS_HOOK_ENTER();

    if (zvfs_real_fstat64(fd, buf) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    if (ZVFS_IN_HOOK()) {
        ZVFS_HOOK_LEAVE();
        return 0;
    }

    pthread_mutex_lock(&g_fs.fd_mu);
    struct zvfs_open_file *of = openfile_lookup(fd);
    pthread_mutex_unlock(&g_fs.fd_mu);

    if (of) {
        zvfs_ensure_init();
        patch_stat64(buf, of->inode);
    }

    ZVFS_HOOK_LEAVE();
    return 0;
}

/* ------------------------------------------------------------------ */
/* lstat（symlink 不穿透；zvfs 不用 symlink，逻辑与 stat 相同）          */
/* ------------------------------------------------------------------ */

int
lstat(const char *path, struct stat *buf)
{
    ZVFS_HOOK_ENTER();

    if (ZVFS_IN_HOOK() || !zvfs_is_zvfs_path(path)) {
        int r = zvfs_real_lstat(path, buf);
        ZVFS_HOOK_LEAVE();
        return r;
    }

    zvfs_ensure_init();

    if (zvfs_real_lstat(path, buf) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    struct zvfs_inode *inode = zvfs_inode_by_path(path);
    if (inode)
        patch_stat(buf, inode);

    ZVFS_HOOK_LEAVE();
    return 0;
}

int
lstat64(const char *path, struct stat64 *buf)
{
    ZVFS_HOOK_ENTER();

    if (ZVFS_IN_HOOK() || !zvfs_is_zvfs_path(path)) {
        int r = zvfs_real_lstat64(path, buf);
        ZVFS_HOOK_LEAVE();
        return r;
    }

    zvfs_ensure_init();

    if (zvfs_real_lstat64(path, buf) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    struct zvfs_inode *inode = zvfs_inode_by_path(path);
    if (inode)
        patch_stat64(buf, inode);

    ZVFS_HOOK_LEAVE();
    return 0;
}

/* ------------------------------------------------------------------ */
/* fstatat / fstatat64                                                  */
/* ------------------------------------------------------------------ */

int
fstatat(int dirfd, const char *path, struct stat *buf, int flags)
{
    ZVFS_HOOK_ENTER();

    char abspath[PATH_MAX];
    int  is_zvfs = 0;

    if (!ZVFS_IN_HOOK()) {
        if (zvfs_resolve_atpath(dirfd, path, abspath, sizeof(abspath)) == 0)
            is_zvfs = zvfs_is_zvfs_path(abspath);
    }

    if (zvfs_real_fstatat(dirfd, path, buf, flags) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    if (is_zvfs) {
        zvfs_ensure_init();
        struct zvfs_inode *inode = zvfs_inode_by_path(abspath);
        if (inode)
            patch_stat(buf, inode);
    }

    ZVFS_HOOK_LEAVE();
    return 0;
}

int
fstatat64(int dirfd, const char *path, struct stat64 *buf, int flags)
{
    ZVFS_HOOK_ENTER();

    char abspath[PATH_MAX];
    int  is_zvfs = 0;

    if (!ZVFS_IN_HOOK()) {
        if (zvfs_resolve_atpath(dirfd, path, abspath, sizeof(abspath)) == 0)
            is_zvfs = zvfs_is_zvfs_path(abspath);
    }

    if (zvfs_real_fstatat64(dirfd, path, buf, flags) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    if (is_zvfs) {
        zvfs_ensure_init();
        struct zvfs_inode *inode = zvfs_inode_by_path(abspath);
        if (inode)
            patch_stat64(buf, inode);
    }

    ZVFS_HOOK_LEAVE();
    return 0;
}

/* ------------------------------------------------------------------ */
/* statx                                                                */
/* ------------------------------------------------------------------ */

int
statx(int dirfd, const char *path, int flags,
      unsigned int mask, struct statx *buf)
{
    ZVFS_HOOK_ENTER();

    if (!real_statx) {
        errno = ENOSYS;
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    char abspath[PATH_MAX];
    int  is_zvfs = 0;

    if (!ZVFS_IN_HOOK()) {
        if (zvfs_resolve_atpath(dirfd, path, abspath, sizeof(abspath)) == 0)
            is_zvfs = zvfs_is_zvfs_path(abspath);
    }

    if (real_statx(dirfd, path, flags, mask, buf) < 0) {
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    if (!is_zvfs) {
        ZVFS_HOOK_LEAVE();
        return 0;
    }

    zvfs_ensure_init();

    /* statx 用 stx_mask 标记哪些字段有效，覆盖 size/atime/mtime */
    struct zvfs_inode *inode = zvfs_inode_by_path(abspath);
    if (inode) {
        pthread_mutex_lock(&inode->mu);

        if (mask & STATX_SIZE) {
            buf->stx_size   = inode->logical_size;
            buf->stx_mask  |= STATX_SIZE;
            /* stx_blocks 以 512 字节为单位 */
            buf->stx_blocks = (inode->logical_size + 511) / 512;
            buf->stx_mask  |= STATX_BLOCKS;
        }
        if (mask & STATX_ATIME) {
            buf->stx_atime.tv_sec  = inode->atime;
            buf->stx_atime.tv_nsec = 0;
            buf->stx_mask |= STATX_ATIME;
        }
        if (mask & STATX_MTIME) {
            buf->stx_mtime.tv_sec  = inode->mtime;
            buf->stx_mtime.tv_nsec = 0;
            buf->stx_mask |= STATX_MTIME;
        }

        pthread_mutex_unlock(&inode->mu);
    }

    ZVFS_HOOK_LEAVE();
    return 0;
}
