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

#include <errno.h>
#include <fcntl.h>
#include <linux/falloc.h>   /* FALLOC_FL_* */
#include <pthread.h>
#include <stdint.h>

/* ------------------------------------------------------------------ */
/* lseek / lseek64                                                      */
/* ------------------------------------------------------------------ */

off_t
lseek(int fd, off_t offset, int whence)
{
    ZVFS_HOOK_ENTER();

    struct zvfs_open_file *of = NULL;
    if (!ZVFS_IN_HOOK()) {
        pthread_mutex_lock(&g_fs.fd_mu);
        of = openfile_lookup(fd);
        pthread_mutex_unlock(&g_fs.fd_mu);
    }

    if (!of) {
        off_t r = real_lseek(fd, offset, whence);
        ZVFS_HOOK_LEAVE();
        return r;
    }

    zvfs_ensure_init();

    /*
     * O_APPEND fd 的 lseek：POSIX 允许 lseek，但下次 write 时
     * 仍会从文件末尾写。lseek 只影响 read 的位置。
     * 我们照常更新 of->offset。
     */
    pthread_mutex_lock(&of->inode->mu);   /* SEEK_END 需读 logical_size */
    uint64_t new_off = openfile_seek(of, (int64_t)offset, whence);
    pthread_mutex_unlock(&of->inode->mu);

    if (new_off == (uint64_t)-1) {
        ZVFS_HOOK_LEAVE();
        return (off_t)-1;
    }

    ZVFS_HOOK_LEAVE();
    return (off_t)new_off;
}

off_t lseek64(int fd, off_t offset, int whence)
{
    return lseek(fd, offset, whence);
}

/* ------------------------------------------------------------------ */
/* 内部：按 inode 指针做 truncate（path / fd 路径共用）                  */
/* ------------------------------------------------------------------ */


/*
 * zvfs_truncate_by_inode - 对有 handle_id 的 openfile 做 truncate。
 * 找到任意一个打开该 inode 的 openfile 取其 handle_id。
 */
static int
zvfs_truncate_inode_with_handle(struct zvfs_inode *inode,
                                 int real_fd, uint64_t new_size)
{
    /* 在 fd_table 里找一个指向该 inode 的 openfile 取 handle_id */
    uint64_t handle_id = 0;
    pthread_mutex_lock(&g_fs.fd_mu);
    struct zvfs_open_file *of, *tmp;
    HASH_ITER(hh, g_fs.fd_table, of, tmp) {
        (void)tmp;
        if (of->inode == inode) {
            handle_id = of->handle_id;
            break;
        }
    }
    pthread_mutex_unlock(&g_fs.fd_mu);

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

    if (new_size != old_size && handle_id != 0) {
        if (blob_resize(handle_id, new_size) < 0) {
            errno = EIO;
            return -1;
        }
    } else if (new_size != old_size && handle_id == 0) {
        /*
         * 文件未被打开：需要临时 blob_open。
         * 这种情况下 truncate(path, ...) 被调用但文件没有 fd。
         */
        uint64_t temp_handle_id = 0;
        if (blob_open(inode->blob_id, &temp_handle_id) < 0) {
            errno = EIO;
            return -1;
        }
        int rc = blob_resize(temp_handle_id, new_size);
        blob_close(temp_handle_id);
        if (rc < 0) { errno = EIO; return -1; }
    }

    pthread_mutex_lock(&inode->mu);
    inode_update_size(inode, real_fd, new_size);
    pthread_mutex_unlock(&inode->mu);

    return 0;
}

/* ------------------------------------------------------------------ */
/* ftruncate / ftruncate64                                              */
/* ------------------------------------------------------------------ */

int
ftruncate(int fd, off_t length)
{
    ZVFS_HOOK_ENTER();

    struct zvfs_open_file *of = NULL;
    if (!ZVFS_IN_HOOK()) {
        pthread_mutex_lock(&g_fs.fd_mu);
        of = openfile_lookup(fd);
        pthread_mutex_unlock(&g_fs.fd_mu);
    }

    if (!of) {
        int r = real_ftruncate(fd, length);
        ZVFS_HOOK_LEAVE();
        return r;
    }

    zvfs_ensure_init();

    if (length < 0) { errno = EINVAL; ZVFS_HOOK_LEAVE(); return -1; }

    int r = zvfs_truncate_inode_with_handle(of->inode, fd, (uint64_t)length);
    ZVFS_HOOK_LEAVE();
    return r;
}

int ftruncate64(int fd, off_t length) { return ftruncate(fd, length); }

/* ------------------------------------------------------------------ */
/* truncate / truncate64（按路径）                                       */
/* ------------------------------------------------------------------ */

int
truncate(const char *path, off_t length)
{
    ZVFS_HOOK_ENTER();

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

    zvfs_ensure_init();

    if (length < 0) { errno = EINVAL; ZVFS_HOOK_LEAVE(); return -1; }

    /* 查 path_cache 拿 inode */
    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) {
        /*
         * inode 不在缓存：文件存在于 FS 但从未被 open。
         * 需要读 xattr 拿 blob_id，临时构建 inode。
         * 最简单的做法：先 real_open，再走 zvfs 路径，再 real_close。
         * 这里直接调 real_truncate 改 st_size，但 blob 不会被截断。
         *
         * 更正确的做法：open + ftruncate + close。
         * 调用方通常不会在 file 未被打开的情况下做 truncate，
         * 所以这里先报 ENOENT（找不到 zvfs inode）作为安全兜底。
         */
        errno = ENOENT;
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    int r = zvfs_truncate_inode_with_handle(inode, -1, (uint64_t)length);

    /* 同步真实文件 st_size（real_truncate 更新磁盘元数据） */
    if (r == 0)
        real_truncate(path, length);

    ZVFS_HOOK_LEAVE();
    return r;
}

int truncate64(const char *path, off_t length) { return truncate(path, length); }

/* ------------------------------------------------------------------ */
/* fallocate                                                            */
/* ------------------------------------------------------------------ */

int
fallocate(int fd, int mode, off_t offset, off_t len)
{
    ZVFS_HOOK_ENTER();

    struct zvfs_open_file *of = NULL;
    if (!ZVFS_IN_HOOK()) {
        pthread_mutex_lock(&g_fs.fd_mu);
        of = openfile_lookup(fd);
        pthread_mutex_unlock(&g_fs.fd_mu);
    }

    if (!of) {
        int r = real_fallocate(fd, mode, offset, len);
        ZVFS_HOOK_LEAVE();
        return r;
    }

    zvfs_ensure_init();

    if (offset < 0 || len <= 0) { errno = EINVAL; ZVFS_HOOK_LEAVE(); return -1; }

    /* FALLOC_FL_PUNCH_HOLE：打孔，暂不支持 */
    if (mode & FALLOC_FL_PUNCH_HOLE) {
        errno = ENOTSUP;
        ZVFS_HOOK_LEAVE();
        return -1;
    }

    /* FALLOC_FL_KEEP_SIZE：预分配但不改变文件逻辑大小，直接返回 0 */
    if (mode & FALLOC_FL_KEEP_SIZE) {
        ZVFS_HOOK_LEAVE();
        return 0;
    }

    /*
     * 普通 fallocate（mode == 0）：
     * 确保 [offset, offset+len) 范围内的空间被"分配"。
     * zvfs 的语义：把 logical_size 扩展到 max(logical_size, offset+len)。
     * 不提前 blob_resize，因为 SPDK cluster 按写入时分配更高效。
     */
    uint64_t new_end = (uint64_t)offset + (uint64_t)len;

    pthread_mutex_lock(&of->inode->mu);
    if (new_end > of->inode->logical_size)
        inode_update_size(of->inode, fd, new_end);
    pthread_mutex_unlock(&of->inode->mu);

    ZVFS_HOOK_LEAVE();
    return 0;
}

/* ------------------------------------------------------------------ */
/* posix_fallocate                                                      */
/* ------------------------------------------------------------------ */

int
posix_fallocate(int fd, off_t offset, off_t len)
{
    ZVFS_HOOK_ENTER();

    struct zvfs_open_file *of = NULL;
    if (!ZVFS_IN_HOOK()) {
        pthread_mutex_lock(&g_fs.fd_mu);
        of = openfile_lookup(fd);
        pthread_mutex_unlock(&g_fs.fd_mu);
    }

    if (!of) {
        int r = real_posix_fallocate(fd, offset, len);
        ZVFS_HOOK_LEAVE();
        return r;
    }

    zvfs_ensure_init();

    /*
     * posix_fallocate 不接受 mode 参数，语义等价于 fallocate(fd, 0, ...)。
     * 注意：posix_fallocate 出错时返回错误码（正值），不设置 errno。
     */
    if (offset < 0 || len <= 0) { ZVFS_HOOK_LEAVE(); return EINVAL; }

    uint64_t new_end = (uint64_t)offset + (uint64_t)len;

    pthread_mutex_lock(&of->inode->mu);
    if (new_end > of->inode->logical_size)
        inode_update_size(of->inode, fd, new_end);
    pthread_mutex_unlock(&of->inode->mu);

    ZVFS_HOOK_LEAVE();
    return 0;
}
