"use strict";

const http = require("http");

function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

class CDPClient {
    constructor(wsUrl) {
        this.wsUrl = wsUrl;
        this.ws = null;
        this.id = 0;
        this.handlers = new Map();
    }

    connect() {
        return new Promise((resolve, reject) => {
            try {
                this.ws = new WebSocket(this.wsUrl);
            } catch (e) {
                reject(e);
                return;
            }

            const timer = setTimeout(() => {
                reject(new Error("CDP connect timeout"));
            }, 8000);

            this.ws.onopen = () => {
                clearTimeout(timer);
                resolve();
            };

            this.ws.onerror = () => {
                clearTimeout(timer);
                reject(new Error("CDP websocket error"));
            };

            this.ws.onmessage = (event) => {
                try {
                    const msg = JSON.parse(event.data.toString());
                    if (!msg.id) return;
                    const cb = this.handlers.get(msg.id);
                    if (cb) cb(msg);
                } catch (_) {}
            };
        });
    }

    call(method, params = {}) {
        return new Promise((resolve, reject) => {
            const id = ++this.id;
            const timer = setTimeout(() => {
                this.handlers.delete(id);
                reject(new Error(`CDP timeout: ${method}`));
            }, 10000);

            this.handlers.set(id, (res) => {
                clearTimeout(timer);
                this.handlers.delete(id);
                if (res.error) reject(new Error(res.error.message));
                else resolve(res.result || {});
            });

            this.ws.send(JSON.stringify({ id, method, params }));
        });
    }

    close() {
        if (this.ws) this.ws.close();
    }
}

function fetchJson(url) {
    return new Promise((resolve) => {
        const req = http.get(url, (res) => {
            let data = "";
            res.on("data", (c) => (data += c));
            res.on("end", () => {
                try {
                    resolve(JSON.parse(data));
                } catch (_) {
                    resolve(null);
                }
            });
        });

        req.on("error", () => resolve(null));
        req.setTimeout(1500, () => {
            req.destroy();
            resolve(null);
        });
    });
}

async function findLocalTarget() {
    const ports = [8888, 9222];
    const endpoints = ["/json", "/json/list"];

    for (const port of ports) {
        for (const ep of endpoints) {
            const targets = await fetchJson(`http://127.0.0.1:${port}${ep}`);
            if (!Array.isArray(targets)) continue;

            const local = targets.find(
                (t) =>
                    (t.url && t.url.startsWith("file://") && t.title === "Pi Desktop") ||
                    (t.type === "page" && t.title === "Pi Desktop")
            );

            if (local && local.webSocketDebuggerUrl) {
                return { local, port };
            }
        }
    }

    return null;
}

async function evalJson(client, expression) {
    const res = await client.call("Runtime.evaluate", {
        expression,
        returnByValue: true,
    });
    return JSON.parse(res?.result?.value || "{}");
}

async function ensureNodeSwitchReady(client) {
    console.log("[Bootstrap] 打开 #/node-switch 页面");
    await client.call("Runtime.evaluate", { expression: "window.location.hash='/node-switch'" });
    await sleep(5000);

    const status = await evalJson(
        client,
        `(function(){
            try {
                var t=(document.body.innerText||'');
                var hasLocal=/Local Block number:?\\s*\\d+/i.test(t);
                var hasLatest=/Latest Block number:?\\s*\\d+/i.test(t);
                var sw=document.querySelector('.react-switch-bg, [role="switch"], input[type="checkbox"]');
                var on=null, x=0, y=0;
                if (sw) {
                    if (sw.tagName==='INPUT') on=!!sw.checked;
                    else on=window.getComputedStyle(sw).backgroundColor.includes('rgb(2');
                    var r=sw.getBoundingClientRect();
                    x=r.left+r.width/2; y=r.top+r.height/2;
                }
                return JSON.stringify({hasLocal:hasLocal, hasLatest:hasLatest, hasSwitch:!!sw, on:on, x:x, y:y});
            } catch(e){
                return JSON.stringify({error:e.message});
            }
        })()`
    );

    if (status.hasLocal && status.hasLatest) {
        console.log("[Bootstrap] 区块高度已可读，无需操作");
        return true;
    }

    if (status.hasSwitch && status.on === false) {
        console.log("[Bootstrap] 区块高度开关未开启，尝试开启");
        await client.call("Input.dispatchMouseEvent", {
            type: "mousePressed",
            x: status.x,
            y: status.y,
            button: "left",
            clickCount: 1,
        });
        await sleep(50);
        await client.call("Input.dispatchMouseEvent", {
            type: "mouseReleased",
            x: status.x,
            y: status.y,
            button: "left",
            clickCount: 1,
        });
        await sleep(1500);
    }

    const verify = await evalJson(
        client,
        `(function(){
            try {
                var t=(document.body.innerText||'');
                return JSON.stringify({
                    hasLocal:/Local Block number:?\\s*\\d+/i.test(t),
                    hasLatest:/Latest Block number:?\\s*\\d+/i.test(t)
                });
            } catch(e){ return JSON.stringify({hasLocal:false,hasLatest:false}); }
        })()`
    );

    return verify.hasLocal && verify.hasLatest;
}

async function ensureApiToggle(client) {
    console.log("[Bootstrap] 打开 #/troubleshooting 页面");
    await client.call("Runtime.evaluate", { expression: "window.location.hash='/troubleshooting'" });
    await sleep(2500);

    const status = await evalJson(
        client,
        `(function(){
            try {
                var sw=document.querySelector('.react-switch-bg, input[type="checkbox"], [role="switch"]');
                if(!sw) return JSON.stringify({found:false});
                var on=null;
                if (sw.tagName==='INPUT') on=!!sw.checked;
                else on=window.getComputedStyle(sw).backgroundColor.includes('rgb(2');
                var r=sw.getBoundingClientRect();
                return JSON.stringify({found:true,on:on,x:r.left+r.width/2,y:r.top+r.height/2});
            } catch(e){ return JSON.stringify({found:false,error:e.message}); }
        })()`
    );

    if (!status.found) {
        console.log("[Bootstrap] 未找到 API 服务开关");
        return false;
    }

    if (status.on === true) {
        console.log("[Bootstrap] API 服务开关已开启");
        return true;
    }

    console.log("[Bootstrap] API 服务开关未开启，尝试开启");
    await client.call("Input.dispatchMouseEvent", {
        type: "mousePressed",
        x: status.x,
        y: status.y,
        button: "left",
        clickCount: 1,
    });
    await sleep(50);
    await client.call("Input.dispatchMouseEvent", {
        type: "mouseReleased",
        x: status.x,
        y: status.y,
        button: "left",
        clickCount: 1,
    });
    await sleep(2000);

    return true;
}

async function waitApiReady() {
    for (let i = 0; i < 8; i++) {
        try {
            const r = await fetch("http://127.0.0.1:31401/");
            if (r.ok) {
                const j = await r.json();
                const v = Number(j.core_latest_ledger || 0);
                if (Number.isFinite(v) && v > 0) return true;
            }
        } catch (_) {}
        await sleep(1000);
    }
    return false;
}

async function main() {
    console.log("[Bootstrap] 一次性初始化开始");

    const found = await findLocalTarget();
    if (!found) {
        console.log("[Bootstrap] 未找到本地 Pi Desktop target，跳过初始化");
        process.exit(2);
        return;
    }

    console.log(`[Bootstrap] 已找到本地 target (CDP:${found.port})`);

    const client = new CDPClient(found.local.webSocketDebuggerUrl);
    try {
        await client.connect();
        await client.call("Page.enable");
        await client.call("Runtime.enable");
        await client.call("DOM.enable");

        const nodeOk = await ensureNodeSwitchReady(client);
        const apiToggleOk = await ensureApiToggle(client);
        const apiReady = await waitApiReady();

        console.log("[Bootstrap] 正在返回首页...");
        await client.call("Runtime.evaluate", { expression: "window.location.hash='/'" });
        await sleep(1000);

        if (nodeOk && apiToggleOk && apiReady) {
            console.log("[Bootstrap] 初始化完成");
            process.exit(0);
            return;
        }

        console.log(`[Bootstrap] 初始化未完全成功 nodeOk=${nodeOk} apiToggleOk=${apiToggleOk} apiReady=${apiReady}`);
        process.exit(3);
    } catch (e) {
        console.log(`[Bootstrap] 初始化异常: ${e.message}`);
        process.exit(4);
    } finally {
        client.close();
    }
}

main();
