const fs = require("node:fs");
const path = require("node:path");
const net = require("node:net");
const { spawn, spawnSync } = require("node:child_process");

const projectRoot = path.resolve(__dirname, "..");
const serverUrl = process.env.VITE_DEV_SERVER_URL || "http://127.0.0.1:3354";
const authServerUrl = process.env.AUTH_SERVER_URL || "http://127.0.0.1:3302";
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
const requiredNodeMajor = 20;
const checkOnly = process.argv.includes("--check");

function log(message = "") {
    process.stdout.write(`${message}\n`);
}

function fail(message) {
    process.stderr.write(`\n[start-local] ${message}\n`);
    process.exit(1);
}

function parseUrl(url, name) {
    try {
        return new URL(url);
    } catch {
        fail(`${name} 无效：${url}`);
    }
}

function checkCommand(command, args, label) {
    const result = spawnSync(command, args, {
        cwd: projectRoot,
        encoding: "utf8",
        stdio: "pipe",
        shell: process.platform === "win32",
    });

    if (result.error || result.status !== 0) {
        fail(`未检测到 ${label}，请先安装后再运行。`);
    }

    return (result.stdout || result.stderr || "").trim();
}

function checkNodeVersion() {
    const major = Number(process.versions.node.split(".")[0]);
    if (!Number.isFinite(major) || major < requiredNodeMajor) {
        fail(`当前 Node.js 为 ${process.version}，建议使用 .nvmrc 指定的 Node.js ${requiredNodeMajor}。`);
    }
}

function checkDependencies() {
    const viteBin = path.join(projectRoot, "node_modules", ".bin", process.platform === "win32" ? "vite.cmd" : "vite");
    const electronPackage = path.join(projectRoot, "node_modules", "electron", "package.json");

    if (!fs.existsSync(viteBin) || !fs.existsSync(electronPackage)) {
        fail("依赖尚未安装，请先执行：npm install");
    }
}

function assertPortAvailable(host, port, label) {
    return new Promise((resolve, reject) => {
        const server = net.createServer();

        server.once("error", error => {
            if (error.code === "EADDRINUSE") {
                reject(new Error(`${label} 端口 ${port} 已被占用，请先关闭已有进程。`));
                return;
            }
            reject(error);
        });

        server.once("listening", () => {
            server.close(resolve);
        });

        server.listen(port, host);
    });
}

function canConnect(host, port) {
    return new Promise(resolve => {
        const socket = net.createConnection({ host, port });
        const finish = ok => {
            socket.destroy();
            resolve(ok);
        };
        socket.setTimeout(500);
        socket.once("connect", () => finish(true));
        socket.once("timeout", () => finish(false));
        socket.once("error", () => finish(false));
    });
}

async function startLocalAuth(authUrl) {
    const authHost = authUrl.hostname || "127.0.0.1";
    const authPort = Number(authUrl.port || 3302);

    if (await canConnect(authHost, authPort)) {
        log(`本地认证服务已在运行：${authServerUrl}`);
        return null;
    }

    const child = spawn(process.execPath, ["scripts/local-auth-server.cjs"], {
        cwd: projectRoot,
        stdio: "inherit",
        env: {
            ...process.env,
            LOCAL_AUTH_HOST: authHost,
            LOCAL_AUTH_PORT: String(authPort),
        },
        shell: false,
    });

    child.on("error", error => {
        fail(`本地认证服务启动失败：${error.message}`);
    });

    for (let attempt = 0; attempt < 40; attempt++) {
        if (await canConnect(authHost, authPort)) {
            return child;
        }
        await new Promise(resolve => setTimeout(resolve, 250));
    }

    child.kill();
    fail(`本地认证服务未能在 ${authServerUrl} 启动。`);
}

async function main() {
    process.chdir(projectRoot);

    const parsedServerUrl = parseUrl(serverUrl, "VITE_DEV_SERVER_URL");
    const parsedAuthUrl = parseUrl(authServerUrl, "AUTH_SERVER_URL");
    const viteHost = parsedServerUrl.hostname || "127.0.0.1";
    const vitePort = Number(parsedServerUrl.port || 3354);

    log("========================================");
    log(" 本地快速启动：Local Auth + Vite + Electron");
    log("========================================");
    log(`项目目录：${projectRoot}`);
    log(`认证服务：${authServerUrl}`);
    log(`开发地址：${serverUrl}`);
    log("");

    checkNodeVersion();
    const npmVersion = checkCommand(npmCommand, ["--version"], "npm");
    checkDependencies();
    await assertPortAvailable(viteHost, vitePort, "Vite");

    log(`Node.js：${process.version}`);
    log(`npm：${npmVersion}`);
    log("");

    if (checkOnly) {
        log("自检通过：可以执行 npm run start:local 或双击 start-local.bat 启动。");
        return;
    }

    const authChild = await startLocalAuth(parsedAuthUrl);

    log("");
    log("正在启动应用，关闭此窗口即可停止开发服务。");
    log("默认本地账号：13800138000 / 123456");
    log("");

    const appChild = spawn(npmCommand, ["run", "dev:electron"], {
        cwd: projectRoot,
        stdio: "inherit",
        env: {
            ...process.env,
            VITE_DEV_SERVER_URL: serverUrl,
            AUTH_SERVER_URL: authServerUrl,
        },
        shell: process.platform === "win32",
    });

    const shutdown = code => {
        if (authChild && !authChild.killed) {
            authChild.kill();
        }
        process.exit(code ?? 0);
    };

    process.on("SIGINT", () => shutdown(0));
    process.on("SIGTERM", () => shutdown(0));

    appChild.on("exit", shutdown);
    appChild.on("error", error => {
        fail(`启动失败：${error.message}`);
    });
}

main().catch(error => {
    fail(error.message);
});
