#include "ipc_reactor.h"
#include "ipc_cq.h"
#include "common/config.h"

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <stdint.h>

static int send_all(int fd, const uint8_t *buf, size_t len) {
    size_t off = 0;

    while (off < len) {
        ssize_t sent = send(fd, buf + off, len - off, 0);
        if (sent > 0) {
            off += (size_t)sent;
            continue;
        }
        if (sent < 0 && errno == EINTR) {
            continue;
        }
        if (sent < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
            /* 当前实现优先功能，等待对端可写后重试。 */
            usleep(100);
            continue;
        }
        return -1;
    }
    return 0;
}

/** ====================================================== */
/* CQ OP                                                   */
/** ====================================================== */
static void cq_consume_send(struct cq *q) {
    struct zvfs_resp *resp;
    while ((resp = CQ_Pop(q)) != NULL) {
        struct zvfs_conn *conn = resp->conn;
        size_t cap = ZVFS_IPC_BUF_SIZE;
        uint8_t *buf = NULL;

        // printf("[resp][%s]\n",cast_opcode2string(resp->opcode));

        buf = malloc(cap);
        if (!buf) {
            fprintf(stderr, "serialize resp failed: alloc %zu bytes\n", cap);
            free(resp->data);
            free(resp);
            continue;
        }

        size_t n = zvfs_serialize_resp(resp, buf, cap);
        if (n == 0 && resp->status == 0 && resp->opcode == ZVFS_OP_READ) {
            if (resp->length <= SIZE_MAX - 64) {
                size_t need = (size_t)resp->length + 64;
                uint8_t *bigger = realloc(buf, need);
                if (bigger) {
                    buf = bigger;
                    cap = need;
                    n = zvfs_serialize_resp(resp, buf, cap);
                }
            }
        }

        if (n == 0) {
            fprintf(stderr, "serialize resp failed: op=%u status=%d len=%lu cap=%zu\n",
                    resp->opcode, resp->status, resp->length, cap);
            free(buf);
            free(resp->data);
            free(resp);
            continue;
        }

        if (send_all(conn->fd, buf, n) != 0) {
            perror("send");
            free(buf);
            free(resp->data);
            free(resp);
            continue;
        }
        free(buf);

        // 清理
        if(resp->data) free(resp->data);
        free(resp);
    }
}

static int set_nonblock(int fd){
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0)
        return -1;

    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

static void epoll_add(struct zvfs_reactor *r, int fd, void *ptr, uint32_t events)
{
    struct epoll_event ev;

    memset(&ev, 0, sizeof(ev));
    ev.events = events;
    ev.data.ptr = ptr;

    epoll_ctl(r->epfd, EPOLL_CTL_ADD, fd, &ev);
}

static void epoll_mod(struct zvfs_reactor *r, int fd, void *ptr, uint32_t events){
    struct epoll_event ev;

    memset(&ev, 0, sizeof(ev));
    ev.events = events;
    ev.data.ptr = ptr;

    epoll_ctl(r->epfd, EPOLL_CTL_MOD, fd, &ev);
}

static void conn_destroy(struct zvfs_conn *c){
    close(c->fd);
    free(c);
}

int zvfs_conn_get_fd(struct zvfs_conn *conn){
    return conn->fd;
}

void zvfs_conn_set_ctx(struct zvfs_conn *conn, void *ctx){
    conn->user_ctx = ctx;
}

void *zvfs_conn_get_ctx(struct zvfs_conn *conn){
    return conn->user_ctx;
}

void zvfs_conn_enable_write(struct zvfs_conn *conn){
    if (conn->want_write)
        return;

    conn->want_write = 1;

    struct zvfs_reactor *r = conn->reactor;

    epoll_mod(r, conn->fd, conn,
              EPOLLIN | EPOLLOUT | EPOLLET);
}

void zvfs_conn_disable_write(struct zvfs_conn *conn){
    if (!conn->want_write)
        return;

    conn->want_write = 0;

    struct zvfs_reactor *r = conn->reactor;

    epoll_mod(r, conn->fd, conn,
              EPOLLIN | EPOLLET);
}

void zvfs_conn_close(struct zvfs_conn *conn){
    struct zvfs_reactor *r = conn->reactor;

    if (r->opts.on_close)
        r->opts.on_close(conn, r->opts.cb_ctx);

    epoll_ctl(r->epfd, EPOLL_CTL_DEL, conn->fd, NULL);

    conn_destroy(conn);
}

/**
 * AF_UNIX      -> Unix Domain Socket
 * SOCK_STREAM  -> 类似 TCP
 * path         -> 通过某个文件进行通信
 */
static int create_listen_socket(const char *path, int backlog){
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0)
        return -1;

    struct sockaddr_un addr;

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);

    unlink(path);

    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
        return -1;

    /*
     * 避免 daemon 由 root 启动时，socket 权限受 umask 影响导致
     * 其它用户（如 postgres）connect() 被 EACCES 拒绝。
     */
    if (chmod(path, 0666) < 0)
        return -1;

    if (listen(fd, backlog) < 0)
        return -1;

    set_nonblock(fd);

    return fd;
}

struct zvfs_reactor *zvfs_reactor_create(const struct zvfs_reactor_opts *opts){
    struct zvfs_reactor *r = calloc(1, sizeof(*r));

    r->opts = *opts;

    r->epfd = epoll_create1(0);

    r->listen_fd = create_listen_socket(
            opts->socket_path,
            opts->backlog);

    epoll_add(r, r->listen_fd, NULL, EPOLLIN);

    return r;
}

static void handle_accept(struct zvfs_reactor *r){
    for (;;) {

        int fd = accept(r->listen_fd, NULL, NULL);

        if (fd < 0) {

            if (errno == EAGAIN || errno == EWOULDBLOCK)
                return;

            return;
        }

        set_nonblock(fd);

        struct zvfs_conn *conn = calloc(1, sizeof(*conn));

        conn->fd = fd;
        conn->reactor = r;

        epoll_add(r, fd, conn, EPOLLIN | EPOLLET);

        if (r->opts.on_accept)
            r->opts.on_accept(conn, r->opts.cb_ctx);
    }
}

int
zvfs_reactor_run(struct zvfs_reactor *r){
    struct epoll_event events[64];

    r->running = 1;

    while (r->running) {

        int n = epoll_wait(r->epfd, events, 64, 0);

        for (int i = 0; i < n; i++) {

            if (events[i].data.ptr == NULL) {

                handle_accept(r);
                continue;
            }

            struct zvfs_conn *conn = events[i].data.ptr;

            if (events[i].events & (EPOLLHUP | EPOLLERR)) {

                zvfs_conn_close(conn);
                continue;
            }

            if ((events[i].events & EPOLLIN) &&
                r->opts.on_read) {

                r->opts.on_read(conn, r->opts.cb_ctx);
            }

            if ((events[i].events & EPOLLOUT) &&
                r->opts.on_write) {

                r->opts.on_write(conn, r->opts.cb_ctx);
            }
        }
        cq_consume_send(g_cq);
    }
    return 0;
}

void zvfs_reactor_stop(struct zvfs_reactor *r){
    r->running = 0;
}


void zvfs_reactor_destroy(struct zvfs_reactor *r){
    close(r->listen_fd);
    close(r->epfd);
    free(r);
}

