#include "common/utils.h"
#include "common/config.h"
#include "spdk_engine.h"
#include "ipc_cq.h"

#include <spdk/event.h>
#include <spdk/log.h>
#include <spdk/bdev.h>
#include <spdk/blob.h>
#include <spdk/blob_bdev.h>
#include <spdk/thread.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <time.h>
#include <stdatomic.h>

/** ===========================================================
 * 全局引擎状态
 * ============================================================ */
struct zvfs_spdk_io_engine g_engine = {0};
static _Atomic bool g_bs_resetting = false;

/** ===========================================================
 * 内部辅助：时钟
 * ============================================================ */
static uint64_t now_mono_ms(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL;
}

/** ===========================================================
 * 内部辅助：错误路径统一 push resp 并释放 req
 * 仅用于无法构造正常 resp 的错误情形
 * ============================================================ */
static void push_err_resp(struct zvfs_req *req, int status) {
    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) {
        SPDK_ERRLOG("push_err_resp: calloc failed, op_code=%u\n", req->opcode);
        if (req->data) free(req->data);
        if (req->add_ref_items) free(req->add_ref_items);
        free(req);
        return;
    }
    resp->opcode     = req->opcode;
    resp->conn       = req->conn;
    resp->status     = status;
    if (req->data) free(req->data);
    if (req->add_ref_items) free(req->add_ref_items);
    free(req);
    CQ_Push(g_cq, resp);
}

/** ===========================================================
 * bdev / blobstore 初始化（仅在 md 线程上执行）
 * 初始化阶段使用同步等待是必要的，因为此时还没有其他线程可以 poll
 * ============================================================ */
struct bs_init_ctx {
    bool                    done;
    int                     rc;
    struct spdk_blob_store *bs;
};

struct json_load_ctx {
    bool done;
    int  rc;
};

static void zvfs_spdk_bdev_event_cb(enum spdk_bdev_event_type type,
                                    struct spdk_bdev *bdev, void *event_ctx) {
    (void)event_ctx;
    if (type == SPDK_BDEV_EVENT_REMOVE)
        SPDK_NOTICELOG("bdev removed: %s\n", spdk_bdev_get_name(bdev));
}

static void bs_init_cb(void *arg, struct spdk_blob_store *bs, int bserrno) {
    struct bs_init_ctx *ctx = arg;
    ctx->rc   = bserrno;
    ctx->bs   = bs;
    ctx->done = true;
}

/**
 * 初始化阶段的同步等待：在 md 线程自身上 poll 直到回调完成
 * 仅在 io_engine_init 阶段合法使用
 */
static int wait_done_init(bool *done_ptr, int *rc_ptr,
                          struct spdk_thread *thread, const char *op) {
    const uint64_t deadline_ms = now_mono_ms() + ZVFS_WAIT_TIME;
    while (!*done_ptr) {
        spdk_thread_poll(thread, 0, 0);
        if (now_mono_ms() >= deadline_ms) {
            SPDK_ERRLOG("%s timeout\n", op);
            return -ETIMEDOUT;
        }
    }
    if (*rc_ptr != 0) {
        SPDK_ERRLOG("%s failed: %d\n", op, *rc_ptr);
        return *rc_ptr;
    }
    return 0;
}

static void json_app_load_done(int rc, void *arg) {
    struct json_load_ctx *ctx = arg;
    ctx->rc   = rc;
    ctx->done = true;
}

static int load_json_config(struct spdk_thread *thread, const char *json_file) {
    const char *path = json_file ? json_file : SPDK_JSON_PATH;

    struct json_load_ctx ctx = {.done = false, .rc = 0};
    spdk_subsystem_init_from_json_config(path, SPDK_DEFAULT_RPC_ADDR,
                                         json_app_load_done, &ctx, true);
    return wait_done_init(&ctx.done, &ctx.rc, thread, "load_json_config");
}

static int open_bdev_and_init_bs(const char *bdev_name, struct spdk_thread *thread) {
    struct spdk_bs_dev *bs_dev = NULL;
    int rc = spdk_bdev_create_bs_dev_ext(bdev_name, zvfs_spdk_bdev_event_cb, NULL, &bs_dev);
    if (rc != 0) {
        SPDK_ERRLOG("spdk_bdev_create_bs_dev_ext failed: %d\n", rc);
        return rc;
    }
    g_engine.bs_dev = bs_dev;

    struct bs_init_ctx ctx = {.done = false, .rc = 0, .bs = NULL};
    spdk_bs_load(bs_dev, NULL, bs_init_cb, &ctx);
    rc = wait_done_init(&ctx.done, &ctx.rc, thread, "bs_load");
    if (rc != 0) {
        SPDK_NOTICELOG("bs_load failed (%d), fallback to bs_init\n", rc);
        bs_dev = NULL;
        rc = spdk_bdev_create_bs_dev_ext(bdev_name, zvfs_spdk_bdev_event_cb, NULL, &bs_dev);
        if (rc != 0) {
            SPDK_ERRLOG("spdk_bdev_create_bs_dev_ext(fallback) failed: %d\n", rc);
            g_engine.bs_dev = NULL;
            return rc;
        }
        g_engine.bs_dev = bs_dev;
        ctx.done = false; ctx.rc = 0; ctx.bs = NULL;
        spdk_bs_init(bs_dev, NULL, bs_init_cb, &ctx);
        rc = wait_done_init(&ctx.done, &ctx.rc, thread, "bs_init");
        if (rc != 0) {
            g_engine.bs_dev = NULL;
            return rc;
        }
    }

    g_engine.bs           = ctx.bs;
    g_engine.io_unit_size = spdk_bs_get_io_unit_size(ctx.bs);
    g_engine.cluster_size = spdk_bs_get_cluster_size(ctx.bs);
    SPDK_NOTICELOG("Blobstore ready on bdev=%s io_unit=%lu cluster=%lu\n",
                   bdev_name, g_engine.io_unit_size, g_engine.cluster_size);
    return 0;
}

/** ===========================================================
 * 线程池 bootstrap
 *   thread_pool[0]       = md 线程（元数据操作）
 *   thread_pool[1..N-1]  = io 线程（数据读写，各自独占一个 channel）
 * ============================================================ */
struct thread_bootstrap_ctx {
    int           idx;
    const char   *bdev_name;   /* 仅 md 线程使用 */
    const char   *json_file;   /* 仅 md 线程使用 */
    pthread_mutex_t mu;
    pthread_cond_t  cv;
    bool            done;
    int             rc;
};

/* md 线程：负责 json 加载、bs 初始化，之后持续 poll */
static void *md_poller_fn(void *arg) {
    struct thread_bootstrap_ctx *boot = arg;
    struct zvfs_io_thread *slot = &g_engine.thread_pool[0];

    spdk_set_thread(slot->thread);

    int rc = load_json_config(slot->thread, boot->json_file);
    if (rc != 0) { SPDK_ERRLOG("load_json_config failed: %d\n", rc); goto notify; }

    rc = open_bdev_and_init_bs(boot->bdev_name, slot->thread);

notify:
    pthread_mutex_lock(&boot->mu);
    boot->rc   = rc;
    boot->done = true;
    pthread_cond_signal(&boot->cv);
    pthread_mutex_unlock(&boot->mu);

    if (rc != 0) return NULL;

    slot->ready = true;
    /* 持续 poll，处理所有通过 spdk_thread_send_msg 分发的 md 操作 */
    while (true) {
        spdk_thread_poll(slot->thread, 0, 0);
        usleep(100);
    }
    return NULL;
}

/* io 线程：等待 bs 就绪后分配 channel，然后持续 poll */
static void *io_poller_fn(void *arg) {
    struct thread_bootstrap_ctx *boot = arg;
    int idx = boot->idx;
    struct zvfs_io_thread *slot = &g_engine.thread_pool[idx];

    spdk_set_thread(slot->thread);

    /* 等待 md 线程完成 blobstore 初始化 */
    const uint64_t deadline_ms = now_mono_ms() + ZVFS_WAIT_TIME * 10;
    while (!g_engine.bs) {
        if (now_mono_ms() >= deadline_ms) {
            SPDK_ERRLOG("io_thread[%d]: wait bs timeout\n", idx);
            pthread_mutex_lock(&boot->mu);
            boot->rc = -ETIMEDOUT; boot->done = true;
            pthread_cond_signal(&boot->cv);
            pthread_mutex_unlock(&boot->mu);
            return NULL;
        }
        usleep(1000);
    }

    slot->channel = spdk_bs_alloc_io_channel(g_engine.bs);
    if (!slot->channel) {
        SPDK_ERRLOG("io_thread[%d]: alloc io_channel failed\n", idx);
        pthread_mutex_lock(&boot->mu);
        boot->rc = -ENOMEM; boot->done = true;
        pthread_cond_signal(&boot->cv);
        pthread_mutex_unlock(&boot->mu);
        return NULL;
    }

    pthread_mutex_lock(&boot->mu);
    boot->rc = 0; boot->done = true;
    pthread_cond_signal(&boot->cv);
    pthread_mutex_unlock(&boot->mu);

    slot->ready = true;
    /* 持续 poll，处理通过 spdk_thread_send_msg 分发的 IO 操作 */
    while (true) {
        spdk_thread_poll(slot->thread, 0, 0);
        usleep(100);
    }
    return NULL;
}

/** ===========================================================
 * io_engine_init
 * ============================================================ */
int io_engine_init(const char *bdev_name, const char *json_file, int thread_num) {
    if (g_engine.bs && g_engine.thread_pool) return 0;

    /* 1. 初始化 SPDK 环境 */
    struct spdk_env_opts env_opts;
    spdk_env_opts_init(&env_opts);
    env_opts.name = "zvfs";
    if (spdk_env_init(&env_opts) != 0) {
        SPDK_ERRLOG("spdk_env_init failed\n");
        return -1;
    }
    spdk_log_set_print_level(SPDK_LOG_NOTICE);
    spdk_log_set_level(SPDK_LOG_NOTICE);
    spdk_log_open(NULL);

    if (spdk_thread_lib_init(NULL, 0) != 0) {
        SPDK_ERRLOG("spdk_thread_lib_init failed\n");
        return -1;
    }

    /* 2. 分配线程池（至少 1 md + 1 io） */
    if (thread_num < 2) thread_num = 2;
    g_engine.thread_count    = thread_num;
    g_engine.io_thread_count = thread_num - 1;
    g_engine.thread_pool     = calloc(thread_num, sizeof(struct zvfs_io_thread));
    if (!g_engine.thread_pool) {
        SPDK_ERRLOG("calloc thread_pool failed\n");
        return -ENOMEM;
    }

    /* 3. 为所有线程预先创建 spdk_thread 对象 */
    for (int i = 0; i < thread_num; i++) {
        char name[32];
        if (i == 0) snprintf(name, sizeof(name), "md_thread");
        else        snprintf(name, sizeof(name), "io_thread_%d", i);

        g_engine.thread_pool[i].thread = spdk_thread_create(name, NULL);
        if (!g_engine.thread_pool[i].thread) {
            SPDK_ERRLOG("spdk_thread_create[%d] failed\n", i);
            return -ENOMEM;
        }
    }

    /* 4. 启动 md 线程，等待其完成 json + bs 初始化 */
    struct thread_bootstrap_ctx md_boot = {
        .idx       = 0,
        .bdev_name = bdev_name,
        .json_file = json_file,
        .done      = false,
        .rc        = 0,
    };
    pthread_mutex_init(&md_boot.mu, NULL);
    pthread_cond_init(&md_boot.cv, NULL);

    pthread_t md_tid;
    if (pthread_create(&md_tid, NULL, md_poller_fn, &md_boot) != 0) {
        SPDK_ERRLOG("pthread_create md_thread failed\n");
        return -1;
    }
    pthread_detach(md_tid);
    g_engine.thread_pool[0].tid = md_tid;

    pthread_mutex_lock(&md_boot.mu);
    while (!md_boot.done) pthread_cond_wait(&md_boot.cv, &md_boot.mu);
    int md_rc = md_boot.rc;
    pthread_mutex_unlock(&md_boot.mu);
    pthread_cond_destroy(&md_boot.cv);
    pthread_mutex_destroy(&md_boot.mu);

    if (md_rc != 0) {
        SPDK_ERRLOG("md init failed: %d\n", md_rc);
        return md_rc;
    }

    /* 5. 启动所有 io 线程，等待各自就绪 */
    struct thread_bootstrap_ctx *io_boots =
        calloc(g_engine.io_thread_count, sizeof(*io_boots));
    if (!io_boots) return -ENOMEM;

    for (int i = 1; i < thread_num; i++) {
        struct thread_bootstrap_ctx *b = &io_boots[i - 1];
        b->idx  = i;
        b->done = false;
        b->rc   = 0;
        pthread_mutex_init(&b->mu, NULL);
        pthread_cond_init(&b->cv, NULL);

        pthread_t io_tid;
        if (pthread_create(&io_tid, NULL, io_poller_fn, b) != 0) {
            SPDK_ERRLOG("pthread_create io_thread[%d] failed\n", i);
            free(io_boots);
            return -1;
        }
        pthread_detach(io_tid);
        g_engine.thread_pool[i].tid = io_tid;
    }

    for (int i = 0; i < g_engine.io_thread_count; i++) {
        pthread_mutex_lock(&io_boots[i].mu);
        while (!io_boots[i].done) pthread_cond_wait(&io_boots[i].cv, &io_boots[i].mu);
        int io_rc = io_boots[i].rc;
        pthread_mutex_unlock(&io_boots[i].mu);
        pthread_cond_destroy(&io_boots[i].cv);
        pthread_mutex_destroy(&io_boots[i].mu);
        if (io_rc != 0) {
            SPDK_ERRLOG("io_thread[%d] init failed: %d\n", i + 1, io_rc);
            free(io_boots);
            return io_rc;
        }
    }
    free(io_boots);

    /* 6. 初始化哈希表锁 */
    g_engine.handle_cache = NULL;
    pthread_mutex_init(&g_engine.cache_mu, NULL);
    
    SPDK_NOTICELOG("io_engine_init done: %d threads (%d io)\n",
                   thread_num, g_engine.io_thread_count);
    return 0;
}

/** ===========================================================
 * 线程选择
 * ============================================================ */
static struct zvfs_io_thread *get_md_thread(void) {
    return &g_engine.thread_pool[0];
}

/* Round-robin 选取 io 线程 */
static struct zvfs_io_thread *pick_io_thread(void) {
    if (g_engine.io_thread_count <= 0) return NULL;
    static _Atomic uint64_t rr = 0;
    uint64_t idx = atomic_fetch_add(&rr, 1);
    int slot = (int)(idx % (uint64_t)g_engine.io_thread_count) + 1; /* [1, N-1] */
    return &g_engine.thread_pool[slot];
}

bool io_engine_reset_in_progress(void) {
    return atomic_load(&g_bs_resetting);
}

static bool engine_handle_cache_empty(void) {
    bool empty;
    pthread_mutex_lock(&g_engine.cache_mu);
    empty = (g_engine.handle_cache == NULL);
    pthread_mutex_unlock(&g_engine.cache_mu);
    return empty;
}

/** ===========================================================
 * 内部操作上下文定义
 * ============================================================ */

/* blob_create 内部需要 create → open → resize → sync_md 四步 */
struct create_chain_ctx {
    struct zvfs_req *req;
    spdk_blob_id     blob_id;
    struct spdk_blob *blob;
};

struct open_ctx {
    struct zvfs_req  *req;
    struct spdk_blob *blob;
};

struct resize_ctx {
    struct zvfs_req         *req;
    struct zvfs_blob_handle *handle;
    uint64_t                 new_size;
};

struct sync_ctx {
    struct zvfs_req         *req;
    struct zvfs_blob_handle *handle;
};

struct close_ctx {
    struct zvfs_req         *req;
    struct zvfs_blob_handle *handle;
};

struct delete_ctx {
    struct zvfs_req *req;
};

struct reset_ctx {
    struct zvfs_req *req;
};

struct io_ctx {
    struct zvfs_req         *req;
    struct zvfs_blob_handle *handle;
    struct spdk_thread      *io_thread;
    struct spdk_io_channel  *channel;
    uint64_t                 lba_off;
    uint64_t                 lba_len;
    uint32_t                 buf_off;
};

struct write_autogrow_ctx {
    struct io_ctx *ioctx;
    uint64_t       new_size;
};

/** ===========================================================
 * blob_create：create → open → resize → sync_md → push resp
 * ============================================================ */
static void create_open_cb(void *arg, struct spdk_blob *blob, int bserrno);
static void create_resize_cb(void *arg, int bserrno);
static void create_sync_cb(void *arg, int bserrno);
static void create_open_start_cb(void *arg, spdk_blob_id blobid, int bserrno);

static void do_blob_create(void *arg) {
    struct create_chain_ctx *cctx = arg;
    struct spdk_blob_opts opts;
    spdk_blob_opts_init(&opts, sizeof(opts));
    spdk_bs_create_blob_ext(g_engine.bs, &opts, create_open_start_cb, cctx);
}

/* create 完成后立即打开 blob */
static void create_open_start_cb(void *arg, spdk_blob_id blobid, int bserrno) {
    struct create_chain_ctx *cctx = arg;
    if (bserrno != 0) {
        SPDK_ERRLOG("create_blob failed: %d\n", bserrno);
        push_err_resp(cctx->req, bserrno);
        free(cctx);
        return;
    }
    cctx->blob_id = blobid;
    spdk_bs_open_blob(g_engine.bs, blobid, create_open_cb, cctx);
}

static void create_open_cb(void *arg, struct spdk_blob *blob, int bserrno) {
    struct create_chain_ctx *cctx = arg;
    if (bserrno != 0) {
        SPDK_ERRLOG("create open_blob failed: %d\n", bserrno);
        push_err_resp(cctx->req, bserrno);
        free(cctx);
        return;
    }
    cctx->blob = blob;

    uint64_t size_hint = cctx->req->size_hint;
    if (size_hint == 0) size_hint = g_engine.cluster_size;

    uint64_t new_clusters = 0;
    if (zvfs_calc_ceil_units(size_hint, g_engine.cluster_size, &new_clusters) != 0) {
        push_err_resp(cctx->req, -EINVAL);
        free(cctx);
        return;
    }

    spdk_blob_resize(blob, new_clusters, create_resize_cb, cctx);
}

static void create_resize_cb(void *arg, int bserrno) {
    struct create_chain_ctx *cctx = arg;
    if (bserrno != 0) {
        SPDK_ERRLOG("create resize failed: %d\n", bserrno);
        spdk_blob_close(cctx->blob, NULL, NULL);
        push_err_resp(cctx->req, bserrno);
        free(cctx);
        return;
    }
    spdk_blob_sync_md(cctx->blob, create_sync_cb, cctx);
}

static void create_sync_cb(void *arg, int bserrno) {
    struct create_chain_ctx *cctx = arg;
    if (bserrno != 0) {
        SPDK_ERRLOG("create sync_md failed: %d\n", bserrno);
        spdk_blob_close(cctx->blob, NULL, NULL);
        push_err_resp(cctx->req, bserrno);
        free(cctx);
        return;
    }

    /* 构造 handle */
    struct zvfs_blob_handle *handle = calloc(1, sizeof(*handle));
    if (!handle) {
        spdk_blob_close(cctx->blob, NULL, NULL);
        push_err_resp(cctx->req, -ENOMEM);
        free(cctx);
        return;
    }
    handle->blob_id      = cctx->blob_id;
    handle->blob         = cctx->blob;
    handle->dma_buf_size = ZVFS_DMA_BUF_SIZE;
    atomic_init(&handle->ref_count, 1);
    handle->dma_buf      = spdk_dma_malloc(ZVFS_DMA_BUF_SIZE, g_engine.io_unit_size, NULL);
    if (!handle->dma_buf) {
        spdk_blob_close(cctx->blob, NULL, NULL);
        free(handle);
        push_err_resp(cctx->req, -ENOMEM);
        free(cctx);
        return;
    }

    /* 构造响应 */
    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) {
        spdk_dma_free(handle->dma_buf);
        spdk_blob_close(cctx->blob, NULL, NULL);
        free(handle);
        push_err_resp(cctx->req, -ENOMEM);
        free(cctx);
        return;
    }
    resp->opcode     = cctx->req->opcode;
    resp->status     = 0;
    resp->conn       = cctx->req->conn;
    resp->blob_id    = cctx->blob_id;

    zvfs_handle_id_t handle_id;
    if (engine_cache_insert(handle, &handle_id) != 0) {
        spdk_dma_free(handle->dma_buf);
        spdk_blob_close(cctx->blob, NULL, NULL);
        free(handle);
        push_err_resp(cctx->req, -ENOMEM);
        free(cctx);
        return;
    }
    resp->handle_id = handle_id;

    free(cctx->req);
    free(cctx);
    CQ_Push(g_cq, resp);
}

int blob_create(struct zvfs_req *req) {
    struct create_chain_ctx *cctx = calloc(1, sizeof(*cctx));
    if (!cctx) { push_err_resp(req, -ENOMEM); return -ENOMEM; }
    cctx->req = req;
    spdk_thread_send_msg(get_md_thread()->thread, do_blob_create, cctx);
    return 0;
}

/** ===========================================================
 * blob_open
 * ============================================================ */
static void blob_open_done_cb(void *arg, struct spdk_blob *blob, int bserrno) {
    struct open_ctx *octx = arg;
    if (bserrno != 0) {
        SPDK_ERRLOG("blob_open failed: %d\n", bserrno);
        push_err_resp(octx->req, bserrno);
        free(octx);
        return;
    }
    
    octx->blob = blob;

    struct zvfs_blob_handle *handle = calloc(1, sizeof(*handle));
    if (!handle) { push_err_resp(octx->req, -ENOMEM); free(octx); return; }
    handle->blob_id      = octx->req->blob_id;
    handle->blob         = blob;
    handle->dma_buf_size = ZVFS_DMA_BUF_SIZE;
    atomic_init(&handle->ref_count, 1);
    handle->dma_buf      = spdk_dma_malloc(ZVFS_DMA_BUF_SIZE, g_engine.io_unit_size, NULL);
    if (!handle->dma_buf) {
        spdk_blob_close(blob, NULL, NULL);
        free(handle);
        push_err_resp(octx->req, -ENOMEM);
        free(octx);
        return;
    }

    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) {
        spdk_dma_free(handle->dma_buf);
        spdk_blob_close(blob, NULL, NULL);
        free(handle);
        push_err_resp(octx->req, -ENOMEM);
        free(octx);
        return;
    }

    resp->opcode     = octx->req->opcode;
    resp->status     = 0;
    resp->conn       = octx->req->conn;
    zvfs_handle_id_t handle_id;
    if (engine_cache_insert(handle, &handle_id) != 0) {
        spdk_dma_free(handle->dma_buf);
        spdk_blob_close(blob, NULL, NULL);
        free(handle);
        push_err_resp(octx->req, -ENOMEM);
        free(octx);
        return;
    }
    resp->handle_id = handle_id;
    resp->size      = spdk_blob_get_num_clusters(octx->blob) * g_engine.cluster_size;

    
    free(octx->req);
    free(octx);
    CQ_Push(g_cq, resp);
}

static void do_blob_open(void *arg) {
    struct open_ctx *octx = arg;
    struct spdk_blob_open_opts opts;
    spdk_blob_open_opts_init(&opts, sizeof(opts));
    spdk_bs_open_blob_ext(g_engine.bs, octx->req->blob_id, &opts, blob_open_done_cb, octx);
}

int blob_open(struct zvfs_req *req) {
    struct open_ctx *octx = calloc(1, sizeof(*octx));
    if (!octx) { push_err_resp(req, -ENOMEM); return -ENOMEM; }
    octx->req = req;
    spdk_thread_send_msg(get_md_thread()->thread, do_blob_open, octx);
    return 0;
}

/** ===========================================================
 * blob_resize
 * ============================================================ */
static void blob_resize_done_cb(void *arg, int bserrno) {
    struct resize_ctx *rctx = arg;

    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) { push_err_resp(rctx->req, -ENOMEM); free(rctx); return; }
    resp->opcode     = rctx->req->opcode;
    resp->status     = bserrno;
    resp->conn       = rctx->req->conn;

    free(rctx->req);
    free(rctx);
    CQ_Push(g_cq, resp);
}

static void do_blob_resize(void *arg) {
    struct resize_ctx *rctx = arg;
    uint64_t new_clusters = 0;
    int rc = zvfs_calc_ceil_units(rctx->new_size, g_engine.cluster_size, &new_clusters);
    if (rc != 0) { push_err_resp(rctx->req, rc); free(rctx); return; }
    spdk_blob_resize(rctx->handle->blob, new_clusters, blob_resize_done_cb, rctx);
}

int blob_resize(struct zvfs_req *req) {
    struct zvfs_blob_handle *handle = req->handle;
    if (!handle) { push_err_resp(req, -EINVAL); return -EINVAL; }

    struct resize_ctx *rctx = calloc(1, sizeof(*rctx));
    if (!rctx) { push_err_resp(req, -ENOMEM); return -ENOMEM; }
    rctx->req      = req;
    rctx->handle   = handle;
    rctx->new_size = req->size_hint;   /* 上层通过 size_hint 传入新大小 */
    spdk_thread_send_msg(get_md_thread()->thread, do_blob_resize, rctx);
    return 0;
}

/** ===========================================================
 * blob_sync_md
 * ============================================================ */
static void blob_sync_md_done_cb(void *arg, int bserrno) {
    struct sync_ctx *sctx = arg;
    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) { push_err_resp(sctx->req, -ENOMEM); free(sctx); return; }
    resp->opcode     = sctx->req->opcode;
    resp->status     = bserrno;
    resp->conn       = sctx->req->conn;

    free(sctx->req);
    free(sctx);
    CQ_Push(g_cq, resp);
}

static void do_blob_sync_md(void *arg) {
    struct sync_ctx *sctx = arg;
    spdk_blob_sync_md(sctx->handle->blob, blob_sync_md_done_cb, sctx);
}

int blob_sync_md(struct zvfs_req *req) {
    struct zvfs_blob_handle *handle = req->handle;
    if (!handle) { push_err_resp(req, -EINVAL); return -EINVAL; }

    struct sync_ctx *sctx = calloc(1, sizeof(*sctx));
    if (!sctx) { push_err_resp(req, -ENOMEM); return -ENOMEM; }
    sctx->req    = req;
    sctx->handle = handle;
    spdk_thread_send_msg(get_md_thread()->thread, do_blob_sync_md, sctx);
    return 0;
}

/** ===========================================================
 * blob_close
 * ============================================================ */
static void blob_close_done_cb(void *arg, int bserrno) {
    struct close_ctx *cctx = arg;
    if (bserrno == 0) {
        engine_cache_remove((zvfs_handle_id_t)(uintptr_t)cctx->handle);
        spdk_dma_free(cctx->handle->dma_buf);
        free(cctx->handle);
    }

    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) { push_err_resp(cctx->req, -ENOMEM); free(cctx); return; }
    resp->opcode     = cctx->req->opcode;
    resp->status     = bserrno;
    resp->conn       = cctx->req->conn;

    free(cctx->req);
    free(cctx);
    CQ_Push(g_cq, resp);
}

static void do_blob_close(void *arg) {
    struct close_ctx *cctx = arg;
    spdk_blob_close(cctx->handle->blob, blob_close_done_cb, cctx);
}

int blob_close(struct zvfs_req *req) {
    struct zvfs_blob_handle *handle = req->handle;
    if (!handle) { push_err_resp(req, -EINVAL); return -EINVAL; }

    while (1) {
        unsigned int old_ref = atomic_load(&handle->ref_count);
        if (old_ref == 0) {
            push_err_resp(req, -EINVAL);
            return -EINVAL;
        }
        if (atomic_compare_exchange_weak(&handle->ref_count, &old_ref, old_ref - 1)) {
            if (old_ref > 1) {
                struct zvfs_resp *resp = calloc(1, sizeof(*resp));
                if (!resp) {
                    push_err_resp(req, -ENOMEM);
                    return -ENOMEM;
                }
                resp->opcode = req->opcode;
                resp->status = 0;
                resp->conn = req->conn;
                free(req);
                CQ_Push(g_cq, resp);
                return 0;
            }
            break;
        }
    }

    struct close_ctx *cctx = calloc(1, sizeof(*cctx));
    if (!cctx) { push_err_resp(req, -ENOMEM); return -ENOMEM; }
    cctx->req    = req;
    cctx->handle = handle;
    spdk_thread_send_msg(get_md_thread()->thread, do_blob_close, cctx);
    return 0;
}

/** ===========================================================
 * blob_delete
 * ============================================================ */
static void blob_delete_done_cb(void *arg, int bserrno) {
    struct delete_ctx *dctx = arg;
    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) { push_err_resp(dctx->req, -ENOMEM); free(dctx); return; }
    resp->opcode     = dctx->req->opcode;
    resp->status     = bserrno;
    resp->conn       = dctx->req->conn;

    free(dctx->req);
    free(dctx);
    CQ_Push(g_cq, resp);
}

static void do_blob_delete(void *arg) {
    struct delete_ctx *dctx = arg;
    spdk_bs_delete_blob(g_engine.bs, dctx->req->blob_id, blob_delete_done_cb, dctx);
}

int blob_delete(struct zvfs_req *req) {
    struct delete_ctx *dctx = calloc(1, sizeof(*dctx));
    if (!dctx) { push_err_resp(req, -ENOMEM); return -ENOMEM; }
    dctx->req = req;
    spdk_thread_send_msg(get_md_thread()->thread, do_blob_delete, dctx);
    return 0;
}

/** ===========================================================
 * blobstore_reset（测试用途）
 * destroy -> init
 * ============================================================ */
static void blobstore_reset_finish(struct reset_ctx *rctx, int status) {
    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) {
        SPDK_ERRLOG("blobstore_reset_finish: calloc resp failed\n");
        if (rctx->req) {
            free(rctx->req);
        }
        free(rctx);
        atomic_store(&g_bs_resetting, false);
        return;
    }

    resp->opcode = rctx->req->opcode;
    resp->status = status;
    resp->conn = rctx->req->conn;

    free(rctx->req);
    free(rctx);
    CQ_Push(g_cq, resp);
    atomic_store(&g_bs_resetting, false);
}

static void blobstore_reset_init_done_cb(void *arg, struct spdk_blob_store *bs, int bserrno) {
    struct reset_ctx *rctx = arg;
    if (bserrno != 0) {
        SPDK_ERRLOG("blobstore_reset: bs_init failed: %d\n", bserrno);
        blobstore_reset_finish(rctx, bserrno);
        return;
    }

    g_engine.bs = bs;
    g_engine.io_unit_size = spdk_bs_get_io_unit_size(bs);
    g_engine.cluster_size = spdk_bs_get_cluster_size(bs);
    SPDK_NOTICELOG("blobstore_reset done: io_unit=%lu cluster=%lu\n",
                   g_engine.io_unit_size, g_engine.cluster_size);
    blobstore_reset_finish(rctx, 0);
}

static void blobstore_reset_destroy_done_cb(void *arg, int bserrno) {
    struct reset_ctx *rctx = arg;
    if (bserrno != 0) {
        SPDK_ERRLOG("blobstore_reset: bs_destroy failed: %d\n", bserrno);
        blobstore_reset_finish(rctx, bserrno);
        return;
    }

    g_engine.bs = NULL;
    g_engine.io_unit_size = 0;
    g_engine.cluster_size = 0;

    spdk_bs_init(g_engine.bs_dev, NULL, blobstore_reset_init_done_cb, rctx);
}

static void do_blobstore_reset(void *arg) {
    struct reset_ctx *rctx = arg;
    if (!g_engine.bs || !g_engine.bs_dev) {
        blobstore_reset_finish(rctx, -EIO);
        return;
    }
    spdk_bs_destroy(g_engine.bs, blobstore_reset_destroy_done_cb, rctx);
}

int blobstore_reset(struct zvfs_req *req) {
    if (!req) {
        return -EINVAL;
    }

    if (atomic_exchange(&g_bs_resetting, true)) {
        push_err_resp(req, -EALREADY);
        return -EALREADY;
    }

    if (!engine_handle_cache_empty()) {
        atomic_store(&g_bs_resetting, false);
        push_err_resp(req, -EBUSY);
        return -EBUSY;
    }

    struct reset_ctx *rctx = calloc(1, sizeof(*rctx));
    if (!rctx) {
        atomic_store(&g_bs_resetting, false);
        push_err_resp(req, -ENOMEM);
        return -ENOMEM;
    }
    rctx->req = req;
    spdk_thread_send_msg(get_md_thread()->thread, do_blobstore_reset, rctx);
    return 0;
}

/** ===========================================================
 * blob_read
 * IO 操作分发到 io 线程，通过该线程专属的 channel 执行
 * ============================================================ */
static void blob_read_done_cb(void *arg, int bserrno) {
    struct io_ctx *ioctx = arg;
    if (bserrno != 0) {
        push_err_resp(ioctx->req, bserrno);
        free(ioctx);
        return;
    }

    /* 从 dma_buf 拷贝到用户 buf */
    memcpy(ioctx->req->data,
           (uint8_t *)ioctx->handle->dma_buf + ioctx->buf_off,
           ioctx->req->length);

    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) { push_err_resp(ioctx->req, -ENOMEM); free(ioctx); return; }
    resp->opcode     = ioctx->req->opcode;
    resp->status     = 0;
    resp->conn       = ioctx->req->conn;
    resp->length     = ioctx->req->length;
    resp->data       = ioctx->req->data;

    free(ioctx->req);
    free(ioctx);
    CQ_Push(g_cq, resp);
}

static void do_blob_read(void *arg) {
    struct io_ctx *ioctx = arg;
    struct zvfs_blob_handle *handle = ioctx->handle;

    uint64_t cur_size = spdk_blob_get_num_clusters(handle->blob) * g_engine.cluster_size;
    if (ioctx->req->offset + ioctx->req->length > cur_size) {
        SPDK_ERRLOG("blob_read out of range: offset=%lu len=%lu size=%lu\n",
                    ioctx->req->offset, ioctx->req->length, cur_size);
        push_err_resp(ioctx->req, -ERANGE);
        free(ioctx);
        return;
    }

    uint64_t lba_off = 0, lba_len = 0;
    uint32_t buf_off = 0;
    int rc = zvfs_calc_io_units(ioctx->req->offset, ioctx->req->length,
                                 g_engine.io_unit_size, &lba_off, &lba_len, &buf_off);
    if (rc != 0) { push_err_resp(ioctx->req, rc); free(ioctx); return; }

    if (lba_len * g_engine.io_unit_size > ZVFS_DMA_BUF_SIZE) {
        SPDK_ERRLOG("blob_read: aligned size exceeds DMA buf\n");
        push_err_resp(ioctx->req, -ENOSPC);
        free(ioctx);
        return;
    }

    ioctx->lba_off = lba_off;
    ioctx->lba_len = lba_len;
    ioctx->buf_off = buf_off;

    spdk_blob_io_read(handle->blob, ioctx->channel, handle->dma_buf,
                      lba_off, lba_len, blob_read_done_cb, ioctx);
}

int blob_read(struct zvfs_req *req) {
    struct zvfs_blob_handle *handle = req->handle;
    if (!handle || !req->data) { push_err_resp(req, -EINVAL); return -EINVAL; }

    struct zvfs_io_thread *iot = pick_io_thread();
    if (!iot || !iot->ready || !iot->channel) {
        push_err_resp(req, -EIO); return -EIO;
    }

    struct io_ctx *ioctx = calloc(1, sizeof(*ioctx));
    if (!ioctx) { push_err_resp(req, -ENOMEM); return -ENOMEM; }
    ioctx->req     = req;
    ioctx->handle  = handle;
    ioctx->channel = iot->channel;

    spdk_thread_send_msg(iot->thread, do_blob_read, ioctx);
    return 0;
}

/** ===========================================================
 * blob_write（read-modify-write）
 * ============================================================ */
static void blob_write_writephase_cb(void *arg, int bserrno) {
    struct io_ctx *ioctx = arg;
    if (bserrno != 0) {
        push_err_resp(ioctx->req, bserrno);
        free(ioctx);
        return;
    }

    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) { push_err_resp(ioctx->req, -ENOMEM); free(ioctx); return; }
    resp->opcode        = ioctx->req->opcode;
    resp->status        = 0;
    resp->conn          = ioctx->req->conn;
    resp->bytes_written = ioctx->req->length;

    free(ioctx->req);
    free(ioctx);
    CQ_Push(g_cq, resp);
}

static void blob_write_readphase_cb(void *arg, int bserrno) {
    struct io_ctx *ioctx = arg;
    if (bserrno != 0) {
        push_err_resp(ioctx->req, bserrno);
        free(ioctx);
        return;
    }

    /* read-modify: 将用户数据覆盖到 dma_buf 的对应区域 */
    memcpy((uint8_t *)ioctx->handle->dma_buf + ioctx->buf_off,
           ioctx->req->data, ioctx->req->length);

    spdk_blob_io_write(ioctx->handle->blob, ioctx->channel, ioctx->handle->dma_buf,
                       ioctx->lba_off, ioctx->lba_len, blob_write_writephase_cb, ioctx);
}

static void do_blob_write(void *arg) {
    struct io_ctx *ioctx = arg;
    struct zvfs_blob_handle *handle = ioctx->handle;

    uint64_t end = 0;
    if (__builtin_add_overflow(ioctx->req->offset, ioctx->req->length, &end)) {
        push_err_resp(ioctx->req, -EOVERFLOW);
        free(ioctx);
        return;
    }

    uint64_t cur_size = spdk_blob_get_num_clusters(handle->blob) * g_engine.cluster_size;
    if (end > cur_size) {
        SPDK_ERRLOG("blob_write out of range: offset=%lu len=%lu size=%lu\n",
                    ioctx->req->offset, ioctx->req->length, cur_size);
        push_err_resp(ioctx->req, -ENOSPC);
        free(ioctx);
        return;
    }

    uint64_t lba_off = 0, lba_len = 0;
    uint32_t buf_off = 0;
    int rc = zvfs_calc_io_units(ioctx->req->offset, ioctx->req->length,
                                 g_engine.io_unit_size, &lba_off, &lba_len, &buf_off);
    if (rc != 0) { push_err_resp(ioctx->req, rc); free(ioctx); return; }

    if (lba_len * g_engine.io_unit_size > ZVFS_DMA_BUF_SIZE) {
        SPDK_ERRLOG("blob_write: aligned size exceeds DMA buf\n");
        push_err_resp(ioctx->req, -ENOSPC);
        free(ioctx);
        return;
    }

    ioctx->lba_off = lba_off;
    ioctx->lba_len = lba_len;
    ioctx->buf_off = buf_off;

    /* 先读出完整的对齐块，再 modify，再写回 */
    spdk_blob_io_read(handle->blob, ioctx->channel, handle->dma_buf,
                      lba_off, lba_len, blob_write_readphase_cb, ioctx);
}

static void write_autogrow_resize_cb(void *arg, int bserrno) {
    struct write_autogrow_ctx *wctx = arg;
    if (bserrno != 0) {
        push_err_resp(wctx->ioctx->req, bserrno);
        free(wctx->ioctx);
        free(wctx);
        return;
    }

    spdk_thread_send_msg(wctx->ioctx->io_thread, do_blob_write, wctx->ioctx);
    free(wctx);
}

static void do_write_autogrow_resize(void *arg) {
    struct write_autogrow_ctx *wctx = arg;
    uint64_t new_clusters = 0;
    int rc = zvfs_calc_ceil_units(wctx->new_size, g_engine.cluster_size, &new_clusters);
    if (rc != 0) {
        push_err_resp(wctx->ioctx->req, rc);
        free(wctx->ioctx);
        free(wctx);
        return;
    }
    spdk_blob_resize(wctx->ioctx->handle->blob, new_clusters, write_autogrow_resize_cb, wctx);
}

int blob_write(struct zvfs_req *req) {
    struct zvfs_blob_handle *handle = req->handle;
    if (!handle || !req->data) { push_err_resp(req, -EINVAL); return -EINVAL; }

    struct zvfs_io_thread *iot = pick_io_thread();
    if (!iot || !iot->ready || !iot->channel) {
        push_err_resp(req, -EIO); return -EIO;
    }

    struct io_ctx *ioctx = calloc(1, sizeof(*ioctx));
    if (!ioctx) { push_err_resp(req, -ENOMEM); return -ENOMEM; }
    ioctx->req     = req;
    ioctx->handle  = handle;
    ioctx->io_thread = iot->thread;
    ioctx->channel = iot->channel;

    uint64_t end = 0;
    if (__builtin_add_overflow(req->offset, req->length, &end)) {
        free(ioctx);
        push_err_resp(req, -EOVERFLOW);
        return -EOVERFLOW;
    }

    uint64_t cur_size = spdk_blob_get_num_clusters(handle->blob) * g_engine.cluster_size;
    if (end > cur_size) {
        if ((req->write_flags & ZVFS_WRITE_F_AUTO_GROW) == 0) {
            free(ioctx);
            push_err_resp(req, -ENOSPC);
            return -ENOSPC;
        }

        struct write_autogrow_ctx *wctx = calloc(1, sizeof(*wctx));
        if (!wctx) {
            free(ioctx);
            push_err_resp(req, -ENOMEM);
            return -ENOMEM;
        }
        wctx->ioctx = ioctx;
        wctx->new_size = end;
        spdk_thread_send_msg(get_md_thread()->thread, do_write_autogrow_resize, wctx);
        return 0;
    }

    spdk_thread_send_msg(iot->thread, do_blob_write, ioctx);
    return 0;
}


/** ===========================================================
 * Destroy
 * ============================================================ */
