"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.uiCommand = uiCommand;
const crypto = __importStar(require("node:crypto"));
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const node_child_process_1 = require("node:child_process");
const chalk_1 = __importDefault(require("chalk"));
const logger_1 = require("../logger");
const platform_1 = require("../platform");
const DEFAULT_PORT = 19422;
function resolveUiDir() {
    // Walk up from this file to find the repo's ui/ folder. When running from
    // dist/commands/ui.js, __dirname is .../dist/commands; from src it is
    // .../src/commands. Either way, ../../ui resolves to the repo root.
    return path.resolve(__dirname, '..', '..', 'ui');
}
function parsePort(raw) {
    if (!raw)
        return DEFAULT_PORT;
    const n = parseInt(raw, 10);
    if (!Number.isFinite(n) || n < 1 || n > 65535) {
        throw new Error(`Invalid --port value: ${raw}`);
    }
    return n;
}
/**
 * Cross-platform spawn for npm/npx-style CLIs. Mirrors the spawnSync wrapper
 * in platform.ts but for the streaming `spawn` API used by long-running procs.
 */
function spawnStreaming(command, args, options) {
    if (!platform_1.isWindows) {
        return (0, node_child_process_1.spawn)(command, args, options);
    }
    const resolved = /\.(cmd|bat)$/i.test(command) ? command : (0, platform_1.findBin)(command);
    const winCmd = (0, platform_1.resolveWindowsCmd)(resolved, args);
    if (winCmd) {
        return (0, node_child_process_1.spawn)(winCmd.bin, winCmd.args, options);
    }
    return (0, node_child_process_1.spawn)(resolved ?? command, args, options);
}
async function uiCommand(options) {
    const port = parsePort(options.port);
    const uiDir = resolveUiDir();
    if (!fs.existsSync(uiDir)) {
        logger_1.logger.error(`UI app not found at ${uiDir}. Reinstall @qelos/aidev or check the repo layout.`);
        process.exit(1);
    }
    const outputEntry = path.join(uiDir, '.output', 'server', 'index.mjs');
    const hasBuild = fs.existsSync(outputEntry);
    const hasDevDeps = fs.existsSync(path.join(uiDir, 'node_modules'));
    // Published npm packages ship only ui/.output (see "files" in package.json),
    // so prod mode is the default whenever the build is present. Dev mode is for
    // the source repo and requires ui/node_modules.
    const prodRequested = options.prod === true;
    let useProd;
    if (prodRequested) {
        if (!hasBuild) {
            logger_1.logger.error(`--prod requested but ${outputEntry} is missing. ` +
                `Build with: cd ${uiDir} && npm run build`);
            process.exit(1);
        }
        useProd = true;
    }
    else if (hasDevDeps) {
        useProd = false;
    }
    else if (hasBuild) {
        useProd = true;
    }
    else {
        logger_1.logger.error(`UI is not runnable. Either install dev deps (cd ${uiDir} && npm install) ` +
            `or build the app (cd ${uiDir} && npm run build).`);
        process.exit(1);
    }
    const token = crypto.randomBytes(32).toString('hex');
    const loginUrl = `http://127.0.0.1:${port}/login?token=${token}`;
    console.log();
    console.log(chalk_1.default.bold('aidev ui'));
    console.log(chalk_1.default.dim('─'.repeat(60)));
    console.log(`  Mode:     ${useProd ? chalk_1.default.cyan('prod') : chalk_1.default.cyan('dev')}`);
    console.log(`  Port:     ${chalk_1.default.cyan(String(port))}`);
    console.log(`  Login:    ${chalk_1.default.green(loginUrl)}`);
    console.log();
    console.log(chalk_1.default.dim('  The token is stored in your browser after login.'));
    console.log(chalk_1.default.dim('  Press Ctrl+C to stop the server.'));
    console.log();
    const env = {
        ...process.env,
        AIDEV_UI_TOKEN: token,
        AIDEV_UI_PORT: String(port),
        AIDEV_CWD: process.cwd(),
        AIDEV_PACKAGE_DIR: path.resolve(__dirname, '..', '..'),
        NITRO_HOST: '127.0.0.1',
        NITRO_PORT: String(port),
    };
    let child;
    if (useProd) {
        child = spawnStreaming(process.execPath, [outputEntry], {
            cwd: uiDir,
            env,
            stdio: 'inherit',
        });
    }
    else {
        child = spawnStreaming('npm', ['run', 'dev'], {
            cwd: uiDir,
            env,
            stdio: 'inherit',
        });
    }
    child.on('error', (err) => {
        if (err.code === 'ENOENT') {
            logger_1.logger.error(useProd
                ? `Failed to launch node at ${process.execPath}`
                : `Failed to launch npm. Install Node.js / npm and try again.`);
        }
        else {
            logger_1.logger.error(`Failed to start UI server: ${err.message}`);
        }
        process.exit(1);
    });
    const forwardSignal = (sig) => {
        if (!child.killed)
            child.kill(sig);
    };
    process.on('SIGINT', () => forwardSignal('SIGINT'));
    process.on('SIGTERM', () => forwardSignal('SIGTERM'));
    child.on('exit', (code, signal) => {
        if (signal === 'SIGINT' || signal === 'SIGTERM') {
            process.exit(0);
        }
        if (code !== null && code !== 0) {
            logger_1.logger.error(`UI server exited with code ${code}. If port ${port} is already in use, ` +
                `re-run with --port <number>.`);
        }
        process.exit(code ?? 0);
    });
}
//# sourceMappingURL=ui.js.map