/*
 * ai_browser_demo.c — eduwin AI Browser 启动入口 demo
 *
 * 内置 WS Hub pub/sub 服务器 + 右侧日志面板（LogView 组件）。
 * 启动后自动注入 jbecool.dom.min.js 并初始化 J.dip，等待 DIP 指令。
 *
 * 用法:
 *   ai_browser_demo.exe                              （默认显示窗口, port=9099）
 *   ai_browser_demo.exe --headless                   （隐身模式）
 *   ai_browser_demo.exe --port 9090                  （自定义 WS 端口）
 *   ai_browser_demo.exe --url https://www.bing.com
 *   ai_browser_demo.exe --help
 *
 * 调试: 运行后看 exe 所在目录的 log.txt（log_info/log_warning 自动写入）
 */

#include "eduwin.h"
#include <string.h>
#include <stdio.h>

/* ── 布局 ── */
#define LOG_PANEL_W  340
#define LOG_VMARGIN    6
#define WIN_W 1200
#define WIN_H 800

static HWND g_wv       = NULL;
static HWND g_win      = NULL;
static HWND g_logview  = NULL;
static int  g_port     = 9099;
static int  g_headless = 0;
static volatile int g_running = 1;
static thread_t *g_ws_thread = NULL;
static WCHAR g_log_buf[4096];
#define LINF(...) do { str_format(g_log_buf,4096,__VA_ARGS__); log_info(g_log_buf); } while(0)
#define LWRN(...) do { str_format(g_log_buf,4096,__VA_ARGS__); log_warning(g_log_buf); } while(0)
#define LERR(...) do { str_format(g_log_buf,4096,__VA_ARGS__); log_error(g_log_buf); } while(0)

/* ── 读取 UTF-8 文件 → WCHAR ── */
static WCHAR *read_file_to_wstr(const WCHAR *path)
{
    FILE *f = _wfopen(path, L"rb");
    if (!f) return NULL;
    fseek(f, 0, SEEK_END);
    long len = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *buf = (char*)malloc(len + 1);
    if (!buf) { fclose(f); return NULL; }
    fread(buf, 1, len, f);
    buf[len] = '\0';
    fclose(f);
    int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0);
    WCHAR *wbuf = (WCHAR*)malloc(wlen * sizeof(WCHAR));
    if (wbuf) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, wlen);
    free(buf);
    return wbuf;
}

/* ════════════════════════════════════════ */
/*  WS Hub 线程                            */
/* ════════════════════════════════════════ */
static void ws_hub_thread_func(void *arg)
{
    int port = *(int*)arg;
    ws_hub_t *hub = ws_hub_create(port);
    if (!hub) { logview_error(g_logview, L"WS Hub 创建失败 (port=%d)", port); return; }
    logview_info(g_logview, L"WS Hub 已启动，监听端口 %d", port);

    char buf[16384];
    WCHAR wbuf[8192];

    while (g_running) {
        int cid = ws_hub_accept(hub, 200);
        if (cid >= 0) { LINF(L"[ws] 新连接 conn_id=%d", cid); logview_info(g_logview, L"新连接 conn_id=%d", cid); }

        int ready = ws_hub_poll(hub, 200);
        if (ready < 0) continue;

        int len = ws_hub_recv(hub, ready, buf, sizeof(buf) - 1, 0);
        if (len <= 0) { LWRN(L"[ws] 断开 conn_id=%d", ready); ws_hub_disconnect(hub, ready); continue; }

        buf[len] = '\0';
        /* log 只保留前 200 字符 */
        {
            char clipped[256]; int i;
            for (i = 0; i < 200 && buf[i]; i++) clipped[i] = buf[i];
            clipped[i] = '\0';
            int wlen = MultiByteToWideChar(CP_UTF8, 0, clipped, -1, wbuf, 8192);
            if (wlen > 0) LINF(L"[ws] conn_id=%d: %s", ready, wbuf);
        }

        json_t *root = cJSON_Parse(buf);
        if (!root) {
            int wlen2 = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, 8192);
            if (wlen2 > 0) LERR(L"[ws] 无效 JSON (conn_id=%d): %s", ready, wbuf);
            continue;
        }

        json_t *type  = cJSON_GetObjectItem(root, "type");
        json_t *topic = cJSON_GetObjectItem(root, "topic");
        if (!type || !cJSON_IsString(type) || !topic || !cJSON_IsString(topic)) {
            cJSON_Delete(root); LWRN(L"[ws] 缺少 type/topic (conn_id=%d)", ready); continue;
        }

        if (strcmp(type->valuestring, "subscribe") == 0) {
            ws_hub_subscribe(hub, ready, topic->valuestring);
            int wlen2 = MultiByteToWideChar(CP_UTF8, 0, topic->valuestring, -1, wbuf, 8192);
            if (wlen2 > 0) LINF(L"[ws] conn_id=%d 订阅 '%s'", ready, wbuf);
        }
        else if (strcmp(type->valuestring, "unsubscribe") == 0) {
            ws_hub_unsubscribe(hub, ready, topic->valuestring);
            int wlen2 = MultiByteToWideChar(CP_UTF8, 0, topic->valuestring, -1, wbuf, 8192);
            if (wlen2 > 0) LINF(L"[ws] conn_id=%d 取消订阅 '%s'", ready, wbuf);
        }
        else if (strcmp(type->valuestring, "publish") == 0) {
            json_t *data = cJSON_GetObjectItem(root, "data");
            if (data) {
                char *payload = cJSON_PrintUnformatted(data);
                if (payload) {
                    int plen = (int)strlen(payload);
                    int n = ws_hub_publish(hub, ready, topic->valuestring, payload, plen);
                    int wlen2 = MultiByteToWideChar(CP_UTF8, 0, topic->valuestring, -1, wbuf, 8192);
                    if (wlen2 > 0) LINF(L"[ws] conn_id=%d 发布到 '%s' (%dB) -> %d 订阅", ready, wbuf, plen, n);
                    cJSON_free(payload);
                }
            }
        }
        cJSON_Delete(root);
    }
    ws_hub_close(hub);
    logview_info(g_logview, L"WS Hub 已关闭");
}

/* ── 前向声明 ── */
static void on_webview_ready_cb(HWND hwv, void *d);
static void on_nav_complete_cb(HWND hwv, int success, int code, void *d);
static void on_webview_message_cb(HWND hwv, const char *json, void *d);
static int  on_window_close_cb(void *d);
static void on_window_resize_cb(int w, int h);

/* ════════════════════════════════════════ */
/*  WebView 回调                           */
/* ════════════════════════════════════════ */
static void on_webview_ready_cb(HWND hwv, void *d)
{
    (void)d;
    LINF(L"[wv] on_webview_ready");
    logview_info(g_logview, L"WebView 就绪");

    WCHAR js_path[MAX_PATH];
    GetModuleFileNameW(NULL, js_path, MAX_PATH);
    WCHAR *p = wcsrchr(js_path, L'\\');
    if (p) wcscpy(p + 1, L"jbecool.dom.min.js");
    else  wcscpy(js_path, L"jbecool.dom.min.js");

    WCHAR *jbecool_js = read_file_to_wstr(js_path);
    if (jbecool_js) {
        LINF(L"[wv] jbecool.dom.min.js 已读入 %d chars，注册到每个新文档", wcslen(jbecool_js));
        webview_add_script_on_doc_create(hwv, jbecool_js);
        free(jbecool_js);
        logview_info(g_logview, L"jbecool.dom.min.js 已注册 (自动注入每个页面)");
    } else {
        LWRN(L"[wv] 未找到 jbecool.dom.min.js");
        logview_warn(g_logview, L"未找到 jbecool.dom.min.js，DIP 不可用");
    }

    const WCHAR *url = cmd_get_def(L"--url", L"about:blank");
    logview_info(g_logview, L"导航到 %s", url);
    webview_navigate(hwv, url);
}

static void on_nav_complete_cb(HWND hwv, int success, int code, void *d)
{
    (void)d;
    LINF(L"[wv] on_nav_complete success=%d code=%d", success, code);
    logview_info(g_logview, L"导航 %s (状态码=%d)", success ? L"成功" : L"失败", code);
    if (!success) return;

    /* 每次导航完成后初始化 DIP（jbecool 由 AddScriptToExecuteOnDocumentCreated 自动注入） */
    WCHAR init_js[256];
    wsprintfW(init_js, L"if(window.J&&J.dip){J.dip.init(%d);window.chrome.webview.postMessage('dip_inited');}else{window.chrome.webview.postMessage('dip_no_J');}", g_port);
    webview_execute_script(hwv, init_js);
    LINF(L"[wv] ExecuteScript: J.dip.init(%d)", g_port);
    logview_info(g_logview, L"J.dip.init(%d) 已发送", g_port);
}

static void on_webview_message_cb(HWND hwv, const char *json, void *d)
{
    (void)hwv; (void)d;
    WCHAR wbuf[2048];
    if (json) {
        MultiByteToWideChar(CP_UTF8, 0, json, -1, wbuf, 2048);
        LINF(L"[wv] 消息: %s", wbuf);
        logview_info(g_logview, L"WebView 消息: %s", wbuf);
    } else {
        LWRN(L"[wv] 消息: (null)");
    }
}

/* ════════════════════════════════════════ */
/*  窗口回调                               */
/* ════════════════════════════════════════ */
static int on_window_close_cb(void *d)
{
    (void)d;
    LINF(L"[app] window close");
    g_running = 0;
    return 1;
}

static void on_window_resize_cb(int w, int h)
{
    int wv_w = w - LOG_PANEL_W;
    if (wv_w < 200) wv_w = 200;
    if (g_wv) SetWindowPos(g_wv, NULL, 0, 0, wv_w, h, SWP_NOZORDER);
    if (g_logview) {
        int lx = wv_w + 2, ly = LOG_VMARGIN;
        int lw = w - lx - 2, lh = h - LOG_VMARGIN * 2;
        if (lw < 100) lw = 100; if (lh < 50) lh = 50;
        SetWindowPos(g_logview, NULL, lx, ly, lw, lh, SWP_NOZORDER);
    }
}

/* ════════════════════════════════════════ */
/*  main                                   */
/* ════════════════════════════════════════ */
int main(void)
{
    log_clear();
    LINF(L"[app] start");

    cmd_init();
    if (cmd_is(L"--help")) { cmd_print_help(L"eduwin AI Browser"); return 0; }
    g_headless = cmd_is(L"--headless");
    g_port = _wtoi(cmd_get_def(L"--port", L"9099"));

    net_init(); webview_init(); init_app(); thread_init();

    if (!g_headless) {
        g_win = create_window(cmd_get_def(L"--title", L"AI Browser"), WIN_W, WIN_H);
        if (!g_win) { alert(L"错误", L"创建主窗口失败"); return 1; }
        on_window_close(g_win, on_window_close_cb, NULL);
        on_window_resize(g_win, on_window_resize_cb);
        g_logview = create_logview(g_win, 0, 0, 0, 0);
        g_wv = create_webview(g_win, 0, 0, WIN_W - LOG_PANEL_W, WIN_H);
        show_window(g_win);
    } else {
        g_win = create_hidden_window(WIN_W, WIN_H);
        g_wv = create_webview(g_win, 0, 0, WIN_W, WIN_H);
    }

    if (!g_wv) { logview_error(g_logview, L"创建 WebView 失败"); return 1; }

    on_webview_ready(g_wv, on_webview_ready_cb, NULL);
    on_navigation_completed(g_wv, on_nav_complete_cb, NULL);
    on_webview_message(g_wv, on_webview_message_cb, NULL);

    g_ws_thread = thread_run(ws_hub_thread_func, &g_port);
    logview_info(g_logview, L"WS Hub 线程已启动 (port=%d)", g_port);

    if (!g_headless) run_app(); else while (g_running) Sleep(200);

    g_running = 0;
    if (g_ws_thread) { thread_join(g_ws_thread, 5000); thread_cleanup(); }
    webview_cleanup();
    if (g_win && g_headless) DestroyWindow(g_win);
    net_cleanup(); cmd_cleanup();
    LINF(L"[app] end");
    return 0;
}
