/**
 * @file    comm_tcp.cpp
 * @brief   通讯抽象层 — TCP 服务器/客户端
 * @details TCP 服务器: socket → bind → listen → accept（epoll 驱动）
 *          TCP 客户端: socket → nonblock-connect → EPOLLOUT 检测完成
 */

#include "comm_internal.h"

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

/**
 * @brief 处理 TCP accept
 */
void handle_accept(stru_comm_instance *inst)
{
    struct sockaddr_in client_addr;
    socklen_t          addr_len = sizeof(client_addr);
    char               ip_str[INET_ADDRSTRLEN];

    while (1)
    {
        int client_fd = accept(inst->listen_fd, (struct sockaddr *)&client_addr, &addr_len);

        if (client_fd == -1)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                break;
            }

            if (inst->debug)
            {
                LOG_E("comm[%d] accept error: %s", inst->id, strerror(errno));
            }
            break;
        }

        set_nonblock(client_fd);

        /* 禁用 Nagle 算法 */
        int opt = 1;

        setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));

        if (epoll_add_fd(client_fd, inst->id, 0) != 0)
        {
            close(client_fd);
            break;
        }

        inst->client_fds.push_back(client_fd);

        inet_ntop(AF_INET, &client_addr.sin_addr, ip_str, sizeof(ip_str));

        if (inst->debug)
        {
            LOG_I("comm[%d] TCP client connected: %s:%d fd=%d",
                     inst->id, ip_str, ntohs(client_addr.sin_port), client_fd);
        }

        notify_state(inst, client_fd, COMM_STATE_CONNECTED);
    }
}

/**
 * @brief TCP 服务器 bind + listen
 */
int tcp_server_bind(stru_comm_instance *inst)
{
    int                fd;
    struct sockaddr_in addr;
    int                opt = 1;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
        return COMM_ERR_SOCKET;
    }

    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    set_nonblock(fd);

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(inst->local_port);

    if (inst->local_ip[0] != '\0')
    {
        inet_pton(AF_INET, inst->local_ip, &addr.sin_addr);
    }
    else
    {
        addr.sin_addr.s_addr = INADDR_ANY;
    }

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        close(fd);

        return COMM_ERR_BIND;
    }

    if (listen(fd, COMM_BACKLOG) == -1)
    {
        close(fd);

        return COMM_ERR_LISTEN;
    }

    if (epoll_add_fd(fd, inst->id, 1) != 0)
    {
        close(fd);

        return COMM_ERR_INTERNAL;
    }

    inst->listen_fd = fd;

    if (inst->debug)
    {
        LOG_I("comm[%d] TCP server listening on %s:%d",
                 inst->id, inst->local_ip, inst->local_port);
    }

    return COMM_OK;
}

/**
 * @brief TCP 客户端 connect（非阻塞）
 */
int tcp_client_connect(stru_comm_instance *inst)
{
    int                fd;
    struct sockaddr_in addr;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
        return COMM_ERR_SOCKET;
    }

    set_nonblock(fd);

    /* 禁用 Nagle 算法 */
    int opt = 1;

    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));

    /* 如果指定了本地 IP:Port，先 bind */
    if (inst->local_ip[0] != '\0' && inst->local_port > 0)
    {
        struct sockaddr_in local_addr;

        memset(&local_addr, 0, sizeof(local_addr));
        local_addr.sin_family = AF_INET;
        local_addr.sin_port   = htons(inst->local_port);
        inet_pton(AF_INET, inst->local_ip, &local_addr.sin_addr);

        if (bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) == -1)
        {
            if (inst->debug)
            {
                LOG_E("comm[%d] TCP client local bind failed: %s", inst->id, strerror(errno));
            }
        }
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port   = htons(inst->remote_port);
    inet_pton(AF_INET, inst->remote_ip, &addr.sin_addr);

    int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));

    if (ret == -1 && errno != EINPROGRESS)
    {
        close(fd);

        return COMM_ERR_CONNECT;
    }

    /* 注册到 epoll，监听 EPOLLOUT 检测连接完成 */
    struct epoll_event ev;

    memset(&ev, 0, sizeof(ev));
    ev.events  = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDHUP;
    ev.data.fd = fd;

    if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1)
    {
        close(fd);

        return COMM_ERR_INTERNAL;
    }

    stru_comm_fd_info info;

    info.fd          = fd;
    info.is_listen   = 0;
    info.instance_id = inst->id;
    g_fd_map[fd]     = info;

    inst->local_fd = fd;

    if (inst->debug)
    {
        LOG_I("comm[%d] TCP client connecting to %s:%d",
                 inst->id, inst->remote_ip, inst->remote_port);
    }

    notify_state(inst, fd, COMM_STATE_CONNECTING);

    return COMM_OK;
}
