#include "spdk_engine_wrapper.h"
#include "spdk_engine.h"
#include "ipc_cq.h"
#include <spdk/log.h>
#include <errno.h>

extern struct zvfs_spdk_io_engine g_engine;

/** cq op */
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);
}

static void push_ok_resp(struct zvfs_req *req) {
    struct zvfs_resp *resp = calloc(1, sizeof(*resp));
    if (!resp) {
        SPDK_ERRLOG("push_ok_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 = 0;
    if (req->data) free(req->data);
    if (req->add_ref_items) free(req->add_ref_items);
    free(req);
    CQ_Push(g_cq, resp);
}

/** hash map op */
int engine_cache_insert(struct zvfs_blob_handle *handle, zvfs_handle_id_t *out_id) {
    struct zvfs_blob_cache_entry *entry = calloc(1, sizeof(*entry));
    if (!entry) return -ENOMEM;
    entry->handle_id = (zvfs_handle_id_t)(uintptr_t)handle;
    entry->handle    = handle;
    pthread_mutex_lock(&g_engine.cache_mu);
    HASH_ADD(hh, g_engine.handle_cache, handle_id, sizeof(zvfs_handle_id_t), entry);
    pthread_mutex_unlock(&g_engine.cache_mu);
    *out_id = entry->handle_id;
    return 0;
}

struct zvfs_blob_handle *engine_cache_lookup(zvfs_handle_id_t handle_id) {
    struct zvfs_blob_cache_entry *entry = NULL;
    pthread_mutex_lock(&g_engine.cache_mu);
    HASH_FIND(hh, g_engine.handle_cache, &handle_id, sizeof(zvfs_handle_id_t), entry);
    pthread_mutex_unlock(&g_engine.cache_mu);
    return entry ? entry->handle : NULL;
}

void engine_cache_remove(zvfs_handle_id_t handle_id) {
    struct zvfs_blob_cache_entry *entry = NULL;
    pthread_mutex_lock(&g_engine.cache_mu);
    HASH_FIND(hh, g_engine.handle_cache, &handle_id, sizeof(zvfs_handle_id_t), entry);
    if (entry) { HASH_DEL(g_engine.handle_cache, entry); free(entry); }
    pthread_mutex_unlock(&g_engine.cache_mu);
}

static int fill_handle(struct zvfs_req *req, const char *op) {
    struct zvfs_blob_handle *handle = engine_cache_lookup(req->handle_id);
    if (!handle) {
        SPDK_ERRLOG("%s: invalid handle_id=%lu\n", op, req->handle_id);
        push_err_resp(req, -EBADF);
        return -EBADF;
    }
    req->handle = handle;
    return 0;
}



// zvfs wrapper 

int zvfs_engine_init(const char *bdev_name, const char *json_file, int thread_num) {
    return io_engine_init(bdev_name, json_file, thread_num);
}

/* create / open：handle 在 engine 回调里注册，wrapper 直接透传 */
static int zvfs_create(struct zvfs_req *req) {
    return blob_create(req);
}

static int zvfs_open(struct zvfs_req *req) {
    return blob_open(req);
}

/* delete：只需要 blob_id，无需 handle */
static int zvfs_delete(struct zvfs_req *req) {
    return blob_delete(req);
}

/* 以下操作需要先填充 handle */
static int zvfs_write(struct zvfs_req *req) {
    if (fill_handle(req, "zvfs_write") != 0) return -EBADF;
    return blob_write(req);
}

static int zvfs_read(struct zvfs_req *req) {
    if (fill_handle(req, "zvfs_read") != 0) return -EBADF;
    return blob_read(req);
}

static int zvfs_resize(struct zvfs_req *req) {
    if (fill_handle(req, "zvfs_resize") != 0) return -EBADF;
    return blob_resize(req);
}

static int zvfs_sync_md(struct zvfs_req *req) {
    if (fill_handle(req, "zvfs_sync_md") != 0) return -EBADF;
    return blob_sync_md(req);
}

/* close：fill_handle 之后 engine 回调里会同步 cache_remove */
static int zvfs_close(struct zvfs_req *req) {
    if (fill_handle(req, "zvfs_close") != 0) return -EBADF;
    return blob_close(req);
}

static int zvfs_add_ref(struct zvfs_req *req) {
    if (req->ref_delta == 0) {
        push_err_resp(req, -EINVAL);
        return -EINVAL;
    }
    if (fill_handle(req, "zvfs_add_ref") != 0) return -EBADF;
    atomic_fetch_add(&req->handle->ref_count, req->ref_delta);
    push_ok_resp(req);
    return 0;
}

static int zvfs_add_ref_batch(struct zvfs_req *req) {
    int rc = 0;
    uint32_t i = 0;

    if (req->add_ref_count == 0 || !req->add_ref_items) {
        push_err_resp(req, -EINVAL);
        return -EINVAL;
    }

    /* TODO: 当前为功能优先的非原子批量加引用实现。 */
    for (i = 0; i < req->add_ref_count; i++) {
        struct zvfs_add_ref_item *item = &req->add_ref_items[i];
        struct zvfs_blob_handle *handle = NULL;

        if (item->ref_delta == 0) {
            rc = -EINVAL;
            continue;
        }

        handle = engine_cache_lookup(item->handle_id);
        if (!handle) {
            rc = -EBADF;
            continue;
        }

        atomic_fetch_add(&handle->ref_count, item->ref_delta);
    }

    if (rc != 0) {
        push_err_resp(req, rc);
        return rc;
    }

    push_ok_resp(req);
    return 0;
}

static int zvfs_reset_blobstore(struct zvfs_req *req) {
    return blobstore_reset(req);
}

int dispatch_to_worker(struct zvfs_req *req){
    if (req->opcode != ZVFS_OP_RESET_BLOBSTORE && io_engine_reset_in_progress()) {
        push_err_resp(req, -EBUSY);
        return -EBUSY;
    }

    switch (req->opcode)
    {
    case ZVFS_OP_CREATE:
        return zvfs_create(req);
    case ZVFS_OP_OPEN:
        return zvfs_open(req);
    case ZVFS_OP_READ:
        return zvfs_read(req);
    case ZVFS_OP_WRITE:
        return zvfs_write(req);
    case ZVFS_OP_RESIZE:
        return zvfs_resize(req);
    case ZVFS_OP_SYNC_MD:
        return zvfs_sync_md(req);
    case ZVFS_OP_CLOSE:
        return zvfs_close(req);
    case ZVFS_OP_DELETE:
        return zvfs_delete(req);
    case ZVFS_OP_ADD_REF:
        return zvfs_add_ref(req);
    case ZVFS_OP_ADD_REF_BATCH:
        return zvfs_add_ref_batch(req);
    case ZVFS_OP_RESET_BLOBSTORE:
        return zvfs_reset_blobstore(req);
    default:
        break;
    }

    return -1;
}
