/**
 * @file mongoose_ws.c
 * @brief WebSocket HAL 实现（Mongoose 版本）
 */

#include "aiagent_defaults.h"
#ifdef WS_IMPL_MONGOOSE

#include "hal_websocket.h"
#include "hal_thread.h"
#include "hal_mutex.h"
#include "hal_system.h"
#include "hal_log.h"
#include "mongoose.h"
#include "mongoose_ws.h"           /* 实现私有可调参数 */
#include <stdlib.h>
#include <string.h>

/* ------------------------------------------------------------------ */
/* Mongoose WebSocket 配置别名                                            */
/* ------------------------------------------------------------------ */

#define TAG                     MGWS_LOG_TAG
#define DEFAULT_RECV_BUF        MGWS_DEFAULT_RECV_BUF
#define POLL_INTERVAL_MS        MGWS_POLL_INTERVAL_MS
#define POLL_SLEEP_MS           MGWS_POLL_SLEEP_MS
#define POLL_THREAD_STACK       MGWS_POLL_THREAD_STACK

/** 轮询线程优先级 */
#define POLL_THREAD_PRIORITY    18U

/** hal_ws_connect 等待 WS 握手完成的最大轮数 */
#define CONNECT_WAIT_ROUNDS     500

/** 每轮等待时长（毫秒） */
#define CONNECT_WAIT_MS         20U

/* ------------------------------------------------------------------ */
/* 私有类型定义                                                        */
/* ------------------------------------------------------------------ */

/**
 * @brief Mongoose WS 连接控制块
 *
 * 每路连接独立持有一个 mg_mgr，天然支持多实例。
 */
typedef struct {
    char                 *url;         /**< 连接 URL（动态分配） */
    hal_ws_callback_t     callback;    /**< 上层事件回调 */
    void                 *user_ctx;    /**< 透传给回调的用户指针 */
    struct mg_mgr         mgr;         /**< mongoose 事件管理器 */
    struct mg_connection *conn;        /**< 当前 WS 连接（弱引用） */
    hal_mutex_t           mutex;       /**< 保护 conn 和 running 标志 */
    hal_thread_t          poll_thread; /**< 轮询线程句柄 */
    volatile bool         connected;  /**< 连接状态标志 */
    volatile bool         running;    /**< 轮询线程运行标志 */
    volatile bool         exited;     /**< 轮询线程已退出标志 */
    volatile uint32_t     last_recv_ms;  /**< 最近收到任何数据/PONG 的时刻 */
    volatile uint32_t     last_ping_ms;  /**< 最近发出 PING 的时刻 */
    volatile bool         ping_enabled;  /**< WS_OPEN 后开启保活，CLOSE/ERROR 关闭 */
    bool                  use_overseas_dns; /**< 使用海外 DNS 解析域名 */
} mg_ws_impl_t;

/* ------------------------------------------------------------------ */
/* 私有函数前向声明                                                    */
/* ------------------------------------------------------------------ */

static void mg_event_handler(struct mg_connection *c, int ev,
                              void *ev_data, void *fn_data);
static void poll_thread_entry(void *arg);

/* ------------------------------------------------------------------ */
/* 私有函数实现                                                        */
/* ------------------------------------------------------------------ */

/**
 * @brief mongoose 事件回调
 *
 * 运行在轮询线程上下文中，将 MG 事件映射到 HAL 事件，
 * 并通知上层 connection_manager。
 *
 * @param c       触发事件的连接对象
 * @param ev      事件类型
 * @param ev_data 事件附带数据（类型随 ev 变化）
 * @param fn_data 创建连接时传入的用户指针（mg_ws_impl_t *）
 */
static void mg_event_handler(struct mg_connection *c, int ev,
                              void *ev_data, void *fn_data)
{
    mg_ws_impl_t *impl = (mg_ws_impl_t *)fn_data;

    if (!impl) {
        return;
    }

    switch (ev) {
    case MG_EV_RESOLVE:
        BD_LOGI(TAG, "ev: RESOLVE (DNS done)");
        break;

    case MG_EV_CONNECT:
        BD_LOGI(TAG, "ev: CONNECT (TCP established)");
        /* wss:// 连接在建立 TCP 后启用 TLS。 */
        if (impl->url && strncmp(impl->url, "wss://", 6) == 0) {
            struct mg_tls_opts opts;
            memset(&opts, 0, sizeof(opts));
            /* ca = NULL → MBEDTLS_SSL_VERIFY_NONE（不校验服务器证书） */
            opts.srvname = mg_url_host(impl->url);  /* SNI */
            mg_tls_init(c, &opts);
        }
        break;

    case MG_EV_HTTP_MSG: {
        const struct mg_http_message *hm =
            (const struct mg_http_message *)ev_data;
        BD_LOGI(TAG, "ev: HTTP_MSG (uri.len=%u, body.len=%u)",
                (unsigned)hm->uri.len, (unsigned)hm->body.len);
        break;
    }

    case MG_EV_WS_OPEN:
        /* WS 握手成功，标记连接状态并通知上层 */
        impl->connected = true;
        /* 启动保活计时：以握手完成时刻为基准 */
        impl->last_recv_ms = hal_system_get_time_ms();
        impl->last_ping_ms = impl->last_recv_ms;
        impl->ping_enabled = true;
        impl->callback(HAL_WS_EVENT_CONNECTED,
                       NULL, 0, impl->user_ctx);
        BD_LOGI(TAG, "ws open");
        break;

    case MG_EV_WS_MSG: {
        /* 收到 WS 数据帧，按 opcode 分发文本/二进制事件 */
        const struct mg_ws_message *wm =
            (const struct mg_ws_message *)ev_data;
        uint8_t op = wm->flags & WEBSOCKET_FLAGS_MASK_OP;

        /* 收到任何业务帧都视为链路存活，刷新保活计时 */
        impl->last_recv_ms = hal_system_get_time_ms();

        if (op == WEBSOCKET_OP_TEXT) {
            impl->callback(HAL_WS_EVENT_TEXT_RECEIVED,
                           wm->data.ptr, wm->data.len,
                           impl->user_ctx);
        } else if (op == WEBSOCKET_OP_BINARY) {
            impl->callback(HAL_WS_EVENT_BINARY_RECEIVED,
                           wm->data.ptr, wm->data.len,
                           impl->user_ctx);
        }
        break;
    }

    case MG_EV_WS_CTL:
        /* 收到控制帧（PONG/PING/CLOSE）即说明链路存活，刷新保活计时 */
        impl->last_recv_ms = hal_system_get_time_ms();
        break;

    case MG_EV_ERROR:
        /* 连接发生错误，通知上层 */
        BD_LOGE(TAG, "ev: ERROR: %s", (const char *)ev_data);
        impl->ping_enabled = false;
        impl->connected = false;
        impl->callback(HAL_WS_EVENT_ERROR,
                       NULL, 0, impl->user_ctx);
        break;

    case MG_EV_CLOSE: {
        /* 连接关闭后通知上层 */
        bool was_connected = impl->connected;
        BD_LOGI(TAG, "ws close (was_connected=%d)", (int)was_connected);
        impl->ping_enabled = false;
        /* 更新连接状态 */
        impl->connected = false;
        impl->conn      = NULL;
        if (was_connected) {
            /* 已建立连接时通知上层断开 */
            impl->callback(HAL_WS_EVENT_DISCONNECTED,
                           NULL, 0, impl->user_ctx);
        } else {
            /* 连接建立前关闭时，结束本次轮询 */
            impl->running = false;
        }
        break;
    }

    default:
        break;
    }
}

/**
 * @brief mongoose 事件循环轮询线程入口
 *
 * 持续调用 mg_mgr_poll()，直到 running 标志被清除。
 * 退出后设置 exited 标志，由外部负责 mg_mgr_free。
 *
 * @param arg  mg_ws_impl_t 指针
 */
static void poll_thread_entry(void *arg)
{
    mg_ws_impl_t *impl = (mg_ws_impl_t *)arg;

    BD_LOGI(TAG, "轮询线程启动");

    while (impl->running) {
        bool   timed_out = false;

        hal_mutex_lock(impl->mutex);
        mg_mgr_poll(&impl->mgr, POLL_INTERVAL_MS);

        /* ----- ping 保活：在轮询线程中统一处理 ----- */
        if (impl->ping_enabled && impl->conn != NULL) {
            uint32_t now = hal_system_get_time_ms();

            if ((uint32_t)(now - impl->last_recv_ms) >= MGWS_PING_TIMEOUT_MS) {
                /* 超时后更新连接状态 */
                impl->ping_enabled = false;
                impl->connected    = false;
                timed_out          = true;
            } else if ((uint32_t)(now - impl->last_ping_ms) >= MGWS_PING_INTERVAL_MS) {
                /* 周期发送 PING，并在收到 PONG 后刷新计时 */
                uint8_t pl[MGWS_PING_PAYLOAD_LEN];
                pl[0] = (uint8_t)(now >> 24);
                pl[1] = (uint8_t)(now >> 16);
                pl[2] = (uint8_t)(now >> 8);
                pl[3] = (uint8_t)(now);
                mg_ws_send(impl->conn, (const char *)pl,
                           sizeof(pl), WEBSOCKET_OP_PING);
                impl->last_ping_ms = now;
            }
        }
        hal_mutex_unlock(impl->mutex);

        /* 通知上层连接已断开 */
        if (timed_out) {
            BD_LOGW(TAG, "ping timeout (>=%ums), disconnect",
                    (unsigned)MGWS_PING_TIMEOUT_MS);
            impl->callback(HAL_WS_EVENT_DISCONNECTED, NULL, 0, impl->user_ctx);
        }

        /* 锁外休眠，让其他线程继续运行 */
        hal_thread_sleep_ms(POLL_SLEEP_MS);
    }

    /* 由执行 poll 的同一线程释放 mgr */
    mg_mgr_free(&impl->mgr);
    BD_LOGI(TAG, "轮询线程退出");
    impl->exited = true;
}

/* ------------------------------------------------------------------ */
/* HAL 接口实现                                                        */
/* ------------------------------------------------------------------ */

hal_ws_handle_t hal_ws_create(const hal_ws_config_t *config)
{
    BD_LOGI(TAG, "WS IMPL: MONGOOSE (third_party/mongoose)");

    if (!config || !config->url || !config->callback) {
        BD_LOGE(TAG, "create: 参数非法");
        return NULL;
    }

    mg_ws_impl_t *impl =
        (mg_ws_impl_t *)calloc(1, sizeof(mg_ws_impl_t));
    if (!impl) {
        BD_LOGE(TAG, "create: 内存不足");
        return NULL;
    }

    size_t url_len = strlen(config->url) + 1;
    impl->url = (char *)malloc(url_len);
    if (!impl->url) {
        free(impl);
        BD_LOGE(TAG, "create: url 内存不足");
        return NULL;
    }
    memcpy(impl->url, config->url, url_len);

    impl->callback  = config->callback;
    impl->user_ctx  = config->user_ctx;
    impl->connected = false;
    impl->running   = false;
    impl->exited    = false;
    impl->conn      = NULL;
    impl->use_overseas_dns = config->use_overseas_dns;

    impl->mutex = hal_mutex_create();
    if (!impl->mutex) {
        free(impl->url);
        free(impl);
        BD_LOGE(TAG, "create: mutex 创建失败");
        return NULL;
    }

    BD_LOGI(TAG, "create ok");
    return (hal_ws_handle_t)impl;
}

int hal_ws_connect(hal_ws_handle_t handle)
{
    if (!handle) {
        return -1;
    }

    mg_ws_impl_t *impl = (mg_ws_impl_t *)handle;

    if (impl->connected) {
        return 0;
    }

    /* 初始化 mongoose 事件管理器 */
    mg_mgr_init(&impl->mgr);
    impl->running = true;

    /* 海外区域使用全球通用 DNS 解析域名 */
    if (impl->use_overseas_dns) {
        impl->mgr.dns4.url   = "udp://223.5.5.5:53";
        impl->mgr.dns4_2.url = "udp://8.8.8.8:53";
    }

    /* 发起 WS 连接请求（异步，握手结果通过 MG_EV_WS_OPEN 回调） */
    impl->conn = mg_ws_connect(&impl->mgr, impl->url,
                               mg_event_handler, impl, NULL);
    if (!impl->conn) {
        BD_LOGE(TAG, "connect: mg_ws_connect 失败");
        impl->running = false;
        mg_mgr_free(&impl->mgr);
        return -1;
    }

    /* 启动轮询线程驱动 mongoose 事件循环 */
    hal_thread_config_t tcfg = {
        .name       = "mg_poll",
        .entry      = poll_thread_entry,
        .arg        = impl,
        .stack_size = POLL_THREAD_STACK,
        .priority   = POLL_THREAD_PRIORITY,
        .queue_size = 0,
    };
    impl->poll_thread = hal_thread_create(&tcfg);
    if (!impl->poll_thread) {
        BD_LOGE(TAG, "connect: 轮询线程创建失败");
        impl->running = false;
        /* 线程未启动，此处手动释放 mgr */
        mg_mgr_free(&impl->mgr);
        return -1;
    }

    /* 等待握手完成（轮询线程会驱动事件，最多等 10 秒）。
     * 若轮询线程在握手期间检测到连接关闭（impl->running = false），
     * 也立即退出，无需等满超时。 */
    int rounds = 0;
    while (!impl->connected && impl->running && rounds < CONNECT_WAIT_ROUNDS) {
        hal_thread_sleep_ms(CONNECT_WAIT_MS);
        rounds++;
    }

    if (!impl->connected) {
        if (rounds >= CONNECT_WAIT_ROUNDS) {
            BD_LOGE(TAG, "connect: 握手超时（%d ms）",
                    CONNECT_WAIT_ROUNDS * (int)CONNECT_WAIT_MS);
        } else {
            BD_LOGE(TAG, "connect: 连接被远端拒绝或关闭");
        }
        /* 通知轮询线程退出，等待其自然结束并释放 mgr */
        impl->running = false;
        while (!impl->exited) {
            hal_thread_sleep_ms(10);
        }
        /* 线程函数已返回，等 OS 调度器完成线程清理 */
        hal_thread_sleep_ms(100);
        hal_thread_destroy(impl->poll_thread);
        impl->poll_thread = NULL;
        return -1;
    }

    BD_LOGI(TAG, "connected: %s", impl->url);
    return 0;
}

int hal_ws_send_text(hal_ws_handle_t handle, const char *text)
{
    if (!handle || !text) {
        return -1;
    }

    mg_ws_impl_t *impl = (mg_ws_impl_t *)handle;

    /* 连接检查与发送操作在同一临界区完成。 */
    hal_mutex_lock(impl->mutex);
    if (!impl->connected || impl->conn == NULL) {
        hal_mutex_unlock(impl->mutex);
        return -1;
    }
    size_t sent = mg_ws_send(impl->conn, text,
                             strlen(text), WEBSOCKET_OP_TEXT);
    hal_mutex_unlock(impl->mutex);

    return (sent > 0u) ? 0 : -1;
}

int hal_ws_send_binary(hal_ws_handle_t handle,
                       const void *data, size_t len)
{
    if (!handle || !data || len == 0u) {
        return -1;
    }

    mg_ws_impl_t *impl = (mg_ws_impl_t *)handle;

    /* 连接检查与发送操作在同一临界区完成。 */
    hal_mutex_lock(impl->mutex);
    if (!impl->connected || impl->conn == NULL) {
        hal_mutex_unlock(impl->mutex);
        return -1;
    }
    size_t sent = mg_ws_send(impl->conn, (const char *)data,
                             len, WEBSOCKET_OP_BINARY);
    hal_mutex_unlock(impl->mutex);

    return (sent > 0u) ? 0 : -1;
}

int hal_ws_disconnect(hal_ws_handle_t handle)
{
    if (!handle) {
        return -1;
    }

    mg_ws_impl_t *impl = (mg_ws_impl_t *)handle;

    /* 通知轮询线程退出，mg_mgr_free 由轮询线程负责 */
    impl->running = false;

    hal_mutex_lock(impl->mutex);
    if (impl->connected && impl->conn != NULL) {
        impl->conn->is_closing = 1;
    }
    impl->connected = false;
    hal_mutex_unlock(impl->mutex);

    return 0;
}

void hal_ws_destroy(hal_ws_handle_t handle)
{
    if (!handle) {
        return;
    }

    mg_ws_impl_t *impl = (mg_ws_impl_t *)handle;

    /* 通知断开 */
    impl->running = false;

    /* 加锁后访问连接对象 */
    hal_mutex_lock(impl->mutex);
    if (impl->connected && impl->conn != NULL) {
        impl->conn->is_closing = 1;
    }
    impl->connected = false;
    hal_mutex_unlock(impl->mutex);

    /* 等待轮询线程退出 */
    if (impl->poll_thread) {
        while (!impl->exited) {
            hal_thread_sleep_ms(10);
        }
        hal_thread_sleep_ms(100);
        hal_thread_destroy(impl->poll_thread);
        impl->poll_thread = NULL;
    }

    hal_mutex_destroy(impl->mutex);
    impl->mutex = NULL;

    free(impl->url);
    impl->url = NULL;

    free(impl);
}

bool hal_ws_is_connected(hal_ws_handle_t handle)
{
    if (!handle) {
        return false;
    }
    return ((const mg_ws_impl_t *)handle)->connected;
}

#endif /* WS_IMPL_MONGOOSE */
