import { resolve, join } from "node:path";
import { createInterface } from "node:readline/promises";
import { detectProject } from "./utils/detect-project.js";
import { runDevPreflight } from "./utils/dev-preflight.js";
import { resolvePreloads } from "./utils/preload.js";
import { ColdRestarter } from "../lib/dev/cold-restarter.js";
import { VextFileWatcher } from "../lib/dev/file-watcher.js";
import { classifyChange } from "../lib/dev/change-classifier.js";
import { shouldUsePolling } from "../lib/dev/detect-polling.js";
async function promptPortConflictDecision(host, port, details, restoreRawMode) {
    if (!process.stdin.isTTY || !process.stdout.isTTY) {
        return "abort";
    }
    process.stdin.setRawMode?.(false);
    const rl = createInterface({ input: process.stdin, output: process.stdout });
    try {
        const owner = details?.pid
            ? ` pid=${details.pid}${details.command ? ` (${details.command})` : ""}`
            : "";
        const answer = await rl.question(`[vext dev] Port ${host ?? "0.0.0.0"}:${port} is in use${owner}. Choose: [r]etry / [k]ill / [n]ext / [a]bort: `);
        switch (answer.trim().toLowerCase()) {
            case "r":
            case "retry":
                return "retry";
            case "k":
            case "kill":
                return "kill";
            case "n":
            case "next":
                return "next";
            default:
                return "abort";
        }
    }
    finally {
        rl.close();
        restoreRawMode?.();
    }
}
function printFileChanges(files, lifecycleLevel) {
    if (lifecycleLevel === "verbose") {
        console.log(`\n[vext dev] ${files.length} file(s) changed:`);
        for (const f of files) {
            const cls = classifyChange(f.path);
            const icon = cls.action === "cold"
                ? "\u{1F534}"
                : cls.action === "soft"
                    ? "\u{1F7E2}"
                    : "\u26AA";
            console.log(`  ${icon} ${f.path} (${f.type})`);
        }
        return;
    }
    const preview = files.slice(0, 3).map((item) => item.path).join(", ");
    const suffix = files.length > 3 ? `, +${files.length - 3} more` : "";
    console.log(`\n[vext dev] ${files.length} file(s) changed${preview ? `: ${preview}${suffix}` : ""}`);
}
// ── 主函数 ──────────────────────────────────────────────────
/**
 * devCommand — vext dev CLI 命令入口
 *
 * 解析命令行参数，启动 ColdRestarter + FileWatcher，
 * 监听文件变更并触发 Cold Restart。
 *
 * @param args 命令行参数（如 ['--poll', '--debounce', '200']）
 */
export async function devCommand(args = []) {
    const options = parseDevArgs(args);
    const hasLifecycleOverride = options.verboseLifecycle === true || process.env.VEXT_LIFECYCLE_LEVEL === "verbose";
    let lifecycleLevel = hasLifecycleOverride
        ? "verbose"
        : "concise";
    let promptActive = false;
    // ── 1. 检测项目结构 ────────────────────────────────────
    const projectRoot = resolve(options.root || process.cwd());
    const project = detectProject(projectRoot);
    // ── 2. 打印欢迎信息 ────────────────────────────────────
    printBanner(options);
    // ── 3. 解析 dev-entry 入口路径 ─────────────────────────
    //
    // dev-entry.js 是 vextjs 框架内部的 dev 子进程入口，
    // 由 tsc 编译到 dist/lib/dev/dev-entry.js。
    //
    // ColdRestarter fork 此文件时，通过 VEXT_ROOT 环境变量
    // 传递用户项目根目录给 devBootstrap。
    //
    const entryScript = join(project.rootDir, "node_modules", "vextjs", "dist", "lib", "dev", "dev-entry.js");
    // ── 4. 创建 ColdRestarter ─────────────────────────────
    const restarterEnv = {
        VEXT_ROOT: project.rootDir,
        VEXT_DEV_MODE: "1",
        NODE_ENV: process.env.NODE_ENV || "development",
    };
    // --port / --host → VEXT_PORT / VEXT_HOST 环境变量传递给子进程
    // loadConfig() 内部读取这些环境变量作为最高优先级覆盖
    if (options.port !== undefined) {
        restarterEnv.VEXT_PORT = String(options.port);
    }
    if (options.host !== undefined) {
        restarterEnv.VEXT_HOST = options.host;
    }
    if (options.portConflict) {
        restarterEnv.VEXT_PORT_CONFLICT = options.portConflict;
    }
    if (options.verboseLifecycle) {
        restarterEnv.VEXT_LIFECYCLE_LEVEL = "verbose";
    }
    // ── 解析预加载模块（vext.preload 字段）──────────────
    //
    // 扫描直接依赖的 package.json vext.preload 字段，
    // 生成 ["--import", "file:///..."] 格式的 execArgv 追加列表。
    // Cold Restart 时 extraExecArgv 自动复用，无需重新计算。
    //
    const preloads = await resolvePreloads(project.rootDir);
    const preloadExecArgv = preloads.flatMap((p) => ["--import", p]);
    const restarterOptions = {
        entryScript,
        env: restarterEnv,
        cwd: project.rootDir,
        extraExecArgv: preloadExecArgv,
    };
    const restarter = new ColdRestarter(restarterOptions);
    const refreshPreloads = async () => {
        const latestPreloads = await resolvePreloads(project.rootDir);
        restarter.setExtraExecArgv(latestPreloads.flatMap((p) => ["--import", p]));
    };
    const runPreflight = async (reason) => {
        const result = await runDevPreflight({
            rootDir: project.rootDir,
            language: project.language,
            reason,
        });
        if (result.ok) {
            return true;
        }
        console.error(`[vext dev] blocking diagnostics found during ${reason}.`);
        console.error("[vext dev] fix the reported issues and save again\n");
        return false;
    };
    // 监听子进程事件
    restarter.setEvents({
        onChildMessage: (msg) => {
            // 子进程可能请求 cold restart（级联检测过大）
            if (typeof msg === "object" &&
                msg !== null &&
                msg.type === "request-cold-restart") {
                const reason = msg.reason ||
                    "child request";
                console.log(`\n[vext dev] child requested cold restart: ${reason}`);
                runPreflight(`child requested cold restart: ${reason}`)
                    .then((ok) => {
                    if (!ok) {
                        return;
                    }
                    return refreshPreloads().then(() => restarter.restart(reason));
                })
                    .catch((err) => {
                    console.error("[vext dev] restart failed:", err instanceof Error ? err.message : err);
                });
                return;
            }
            if (typeof msg === "object" &&
                msg !== null &&
                msg.type === "lifecycle-config" &&
                !hasLifecycleOverride) {
                lifecycleLevel =
                    msg.level === "verbose"
                        ? "verbose"
                        : "concise";
                return;
            }
            if (typeof msg === "object" &&
                msg !== null &&
                msg.type === "port-conflict") {
                promptActive = true;
                promptPortConflictDecision(msg.host, msg.port, msg.details, () => {
                    if (process.stdin.isTTY) {
                        process.stdin.setRawMode(true);
                    }
                })
                    .then((action) => {
                    restarter.sendToChild({ type: "port-conflict-decision", action });
                })
                    .finally(() => {
                    promptActive = false;
                });
            }
        },
        onChildExit: (code, signal) => {
            // 子进程异常退出（非 restart 触发的 kill）
            if (signal) {
                console.error(`\n[vext dev] worker terminated by signal ${signal}`);
            }
            else if (code !== null && code !== 0) {
                console.error(`\n[vext dev] worker exited with code ${code}`);
                console.error("[vext dev] fix the error and save to trigger restart");
            }
        },
    });
    // ── 5. 首次启动 ────────────────────────────────────────
    console.log("[vext dev] starting initial compilation + server...");
    try {
        if (!(await runPreflight("initial start"))) {
            console.error("[vext dev] initial checks failed. Waiting for changes...\n");
        }
        else {
            await refreshPreloads();
            await restarter.restart("initial start");
            console.log("[vext dev] server ready\n");
        }
    }
    catch (err) {
        console.error("[vext dev] initial start failed:", err instanceof Error ? err.message : err);
        console.error("[vext dev] fix the error and save a file to retry\n");
        // 不退出 — 等待用户修复后 FileWatcher 触发 restart
    }
    // ── 6. 创建并启动 FileWatcher ──────────────────────────
    const usePolling = options.poll ?? shouldUsePolling();
    const watcher = new VextFileWatcher({
        root: project.rootDir,
        debounce: options.debounce ?? 0,
        usePolling,
        pollInterval: options.pollInterval ?? 1000,
    });
    watcher.on("change", async (event) => {
        try {
            // ── 打印变更详情 ──────────────────────────────────
            printFileChanges(event.files, lifecycleLevel);
            if (options.clear) {
                console.clear();
            }
            const preflightReason = event.action === "cold" ? "cold restart preflight" : "soft reload preflight";
            if (!(await runPreflight(preflightReason))) {
                return;
            }
            // ── Tier 3: 配置/插件变更 → Cold Restart ─────────
            //
            // 配置文件、插件、.env、package.json、tsconfig.json
            // 的变更影响全局初始化阶段，无法在进程内安全热替换，
            // 必须执行完整的 kill + fork。
            //
            if (event.action === "cold") {
                console.log("[vext dev] config/plugin change detected \u2192 cold restart (Tier 3)...");
                try {
                    await refreshPreloads();
                    await restarter.restart(event.files.map((f) => f.path).join(", "));
                    console.log("[vext dev] cold restart complete\n");
                }
                catch (err) {
                    console.error("[vext dev] restart failed:", err instanceof Error ? err.message : err);
                    console.error("[vext dev] fix the error and save to retry\n");
                }
                return;
            }
            // ── --no-hot 降级：soft 变更也走 Cold Restart ────
            //
            // 用户通过 --no-hot 或 VEXT_DEV_NO_HOT=1 禁用 soft reload，
            // 所有变更都走 Cold Restart 路径。
            //
            if (options.noHot) {
                const hasStructural = event.files.some((f) => f.type === "add" || f.type === "delete");
                console.log(`[vext dev] source change detected \u2192 cold restart (--no-hot) ` +
                    `[${hasStructural ? "structural" : "code"}]...`);
                try {
                    await refreshPreloads();
                    await restarter.restart(event.files.map((f) => f.path).join(", "));
                    console.log("[vext dev] cold restart complete\n");
                }
                catch (err) {
                    console.error("[vext dev] restart failed:", err instanceof Error ? err.message : err);
                    console.error("[vext dev] fix the error and save to retry\n");
                }
                return;
            }
            // ── Tier 1/2: 业务代码变更 → IPC Soft Reload ────
            //
            // Tier 1 (modify): esbuild.transform() 单文件编译 (~3ms, O(1))
            // Tier 2 (add/delete): ctx.rebuild() 全量增量编译 (~80ms)
            //
            // 通过 IPC 发送 { type: 'reload', files: [...] } 消息
            // 到子进程，由 SoftReloader 执行完整的热重载流程。
            //
            const hasStructural = event.files.some((f) => f.type === "add" || f.type === "delete");
            const tier = hasStructural ? "T2:structural" : "T1:code";
            console.log(`[vext dev] source change detected \u2192 soft reload [${tier}]...`);
            restarter.sendToChild({
                type: "reload",
                files: event.files,
            });
        }
        catch (err) {
            console.error("[vext dev] unexpected error in file watcher handler:", err instanceof Error ? err.message : err);
            if (err instanceof Error && err.stack) {
                console.error(err.stack);
            }
            console.error("[vext dev] fix the error and save again to retry\n");
        }
    });
    await watcher.start();
    if (usePolling) {
        console.log("[vext dev] using polling mode " +
            `(interval: ${options.pollInterval ?? 1000}ms)`);
    }
    // ── 7. 优雅退出 ────────────────────────────────────────
    let isCleaningUp = false;
    const cleanup = async () => {
        if (isCleaningUp)
            return;
        isCleaningUp = true;
        console.log("\n[vext dev] shutting down...");
        watcher.stop();
        try {
            await restarter.kill();
        }
        catch {
            // 静默忽略 kill 错误
        }
        process.exit(0);
    };
    process.on("SIGINT", () => {
        cleanup().catch(() => process.exit(1));
    });
    process.on("SIGTERM", () => {
        cleanup().catch(() => process.exit(1));
    });
    // ── 8. 键盘交互 ────────────────────────────────────────
    //
    // 仅在 TTY 环境下（交互式终端）启用：
    //   r — 手动触发 cold restart
    //   c — 清空控制台
    //   h — 打印帮助
    //   Ctrl+C (0x03) — 退出
    //
    if (process.stdin.isTTY) {
        process.stdin.setRawMode(true);
        process.stdin.resume();
        process.stdin.setEncoding("utf-8");
        process.stdin.on("data", (key) => {
            switch (key) {
                case "r":
                    if (promptActive)
                        break;
                    console.log("\n[vext dev] manual cold restart...");
                    runPreflight("manual cold restart")
                        .then((ok) => {
                        if (!ok) {
                            return;
                        }
                        return refreshPreloads().then(() => restarter.restart("manual"));
                    })
                        .catch((err) => {
                        console.error("[vext dev] restart failed:", err instanceof Error ? err.message : err);
                    });
                    break;
                case "c":
                    if (promptActive)
                        break;
                    console.clear();
                    break;
                case "h":
                    if (promptActive)
                        break;
                    // 手动触发 soft reload（全文件）
                    console.log("\n[vext dev] manual soft reload (all sources)...");
                    runPreflight("manual soft reload")
                        .then((ok) => {
                        if (!ok) {
                            return;
                        }
                        restarter.sendToChild({
                            type: "reload",
                            files: [{ path: "src/", type: "modify" }],
                        });
                    })
                        .catch((err) => {
                        console.error("[vext dev] reload failed:", err instanceof Error ? err.message : err);
                    });
                    break;
                case "?":
                    if (promptActive)
                        break;
                    printKeyboardHelp();
                    break;
                case "\x03": // Ctrl+C
                    cleanup().catch(() => process.exit(1));
                    break;
                default:
                    // 忽略其他按键
                    break;
            }
        });
    }
}
// ── 参数解析 ────────────────────────────────────────────────
/**
 * parseDevArgs — 解析 vext dev 的命令行参数
 *
 * 支持的参数：
 *   --root <path>         项目根目录
 *   --poll                强制使用 polling 模式
 *   --poll-interval <ms>  Polling 间隔
 *   --debounce <ms>       防抖间隔
 *   --no-hot              禁用 soft reload
 *   --clear               每次 reload 后清空控制台
 *
 * 使用手动解析（不引入第三方 CLI 库），保持零依赖。
 *
 * @param args 命令行参数
 * @returns 解析后的选项
 */
function parseDevArgs(args) {
    const options = {};
    for (let i = 0; i < args.length; i++) {
        const arg = args[i];
        switch (arg) {
            case "--root":
                if (i + 1 < args.length) {
                    options.root = args[++i];
                }
                break;
            case "--port":
                if (i + 1 < args.length) {
                    const portStr = args[++i];
                    const port = parseInt(portStr, 10);
                    if (Number.isNaN(port) || port < 1 || port > 65535) {
                        console.error(`[vextjs] Invalid port number: "${portStr}"`);
                        process.exit(1);
                    }
                    options.port = port;
                }
                break;
            case "--host":
                if (i + 1 < args.length) {
                    options.host = args[++i];
                }
                break;
            case "--poll":
                options.poll = true;
                break;
            case "--poll-interval":
                if (i + 1 < args.length) {
                    i++;
                    const val = parseInt(args[i] ?? "", 10);
                    if (!Number.isNaN(val) && val > 0) {
                        options.pollInterval = val;
                    }
                    else {
                        console.error(`[vextjs] Invalid --poll-interval value: "${args[i]}"`);
                        process.exit(1);
                    }
                }
                break;
            case "--debounce":
                if (i + 1 < args.length) {
                    i++;
                    const val = parseInt(args[i] ?? "", 10);
                    if (!Number.isNaN(val) && val >= 0) {
                        options.debounce = val;
                    }
                    else {
                        console.error(`[vextjs] Invalid --debounce value: "${args[i]}"`);
                        process.exit(1);
                    }
                }
                break;
            case "--no-hot":
                options.noHot = true;
                break;
            case "--verbose-lifecycle":
                options.verboseLifecycle = true;
                break;
            case "--port-conflict":
                if (i + 1 < args.length) {
                    const strategy = args[++i];
                    if (strategy !== "error" &&
                        strategy !== "prompt" &&
                        strategy !== "kill" &&
                        strategy !== "next") {
                        console.error(`[vextjs] Invalid --port-conflict value: "${strategy}"`);
                        process.exit(1);
                    }
                    options.portConflict = strategy;
                }
                break;
            case "--clear":
                options.clear = true;
                break;
            case "--help":
            case "-h":
                printDevHelp();
                process.exit(0);
                break;
            default:
                if (arg?.startsWith("--")) {
                    console.error(`[vextjs] Unknown option: "${arg}"\n`);
                    printDevHelp();
                    process.exit(1);
                }
                break;
        }
    }
    // 环境变量覆盖（优先级低于 CLI 参数）
    if (options.poll === undefined && process.env.VEXT_DEV_POLL === "1") {
        options.poll = true;
    }
    if (options.poll === undefined && process.env.VEXT_DEV_POLL === "0") {
        options.poll = false;
    }
    if (options.noHot === undefined && process.env.VEXT_DEV_NO_HOT === "1") {
        options.noHot = true;
    }
    if (options.debounce === undefined && process.env.VEXT_DEV_DEBOUNCE) {
        const val = parseInt(process.env.VEXT_DEV_DEBOUNCE, 10);
        if (!Number.isNaN(val) && val >= 0) {
            options.debounce = val;
        }
    }
    if (options.verboseLifecycle === undefined &&
        process.env.VEXT_VERBOSE_LIFECYCLE === "1") {
        options.verboseLifecycle = true;
    }
    if (options.portConflict === undefined &&
        process.env.VEXT_PORT_CONFLICT &&
        ["error", "prompt", "kill", "next"].includes(process.env.VEXT_PORT_CONFLICT)) {
        options.portConflict = process.env.VEXT_PORT_CONFLICT;
    }
    return options;
}
// ── 输出函数 ────────────────────────────────────────────────
/**
 * 打印启动横幅
 */
function printBanner(options) {
    const polling = options.poll ? "polling" : "fs.watch";
    const debounce = options.debounce ?? 0;
    const mode = options.noHot
        ? "Cold Restart (--no-hot)"
        : "Soft Reload + Cold Restart";
    console.log(`
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
\u2551               Vext Dev Server                \u2551
\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
\u2551  Mode: ${mode.padEnd(39)}\u2551
\u2551  Watch: ${polling.padEnd(38)}\u2551
\u2551  Debounce: ${String(`${debounce}ms`).padEnd(35)}\u2551
\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
\u2551  \u{1F7E2} T1 (code):   soft reload (transform)    \u2551
\u2551  \u{1F7E1} T2 (struct): soft reload (rebuild)      \u2551
\u2551  \u{1F534} T3 (cold):   cold restart               \u2551
\u2551  \u26AA ignored:     skip                        \u2551
\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
\u2551  r=restart  h=reload  c=clear  ?=help  ^C=quit\u2551
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
`);
}
/**
 * 打印键盘快捷键帮助
 */
function printKeyboardHelp() {
    console.log(`
  Keyboard shortcuts:
    r       Manual cold restart (kill + fork)
    h       Manual soft reload (all sources)
    c       Clear console
    ?       Show this help
    Ctrl+C  Quit dev server
`);
}
/**
 * 打印 vext dev 的帮助信息
 */
function printDevHelp() {
    console.log(`
  Usage: vext dev [options]

  Start the application in development mode with hot reload.

  Reload strategy (Tier 1/2/3):
    T1  Code changes (modify)    → soft reload via esbuild.transform()
    T2  Structural (add/delete)  → soft reload via ctx.rebuild()
    T3  Config/plugin/.env       → cold restart (kill + fork)

  Options:
    --port <number>       Override the listening port
    --host <string>       Override the listening host
    --root <path>         Project root directory (default: cwd)
    --poll                Force polling mode (for Docker / NFS)
    --poll-interval <ms>  Polling interval in ms (default: 1000)
    --debounce <ms>       Debounce interval in ms (default: 0, disabled)
    --no-hot              Disable soft reload, always cold restart
    --port-conflict <error|prompt|kill|next>
                           Configure how port conflicts are handled
    --verbose-lifecycle   Show verbose lifecycle logs
    --clear               Clear console on each reload
    -h, --help            Show this help message

  Examples:
    $ vext dev
    $ vext dev --port 8080
    $ vext dev --host 127.0.0.1 --port 3000
    $ vext dev --poll --poll-interval 2000
    $ vext dev --debounce 50
    $ vext dev --no-hot
    $ vext dev --port-conflict prompt

  Environment variables:
    VEXT_DEV_POLL=1       Force polling mode
    VEXT_DEV_POLL=0       Force disable polling
    VEXT_DEV_NO_HOT=1     Disable soft reload
    VEXT_DEV_DEBOUNCE=50  Set debounce interval (ms, default: 0)
    VEXT_PORT_CONFLICT=next
    VEXT_VERBOSE_LIFECYCLE=1
`);
}
