/**
 * @module        infra/spawn
 * @description   ProcessSpawner port —— 只用于启动 supervisor（长跑 sps tick 进程）
 *
 * @layer         infra
 *
 * 注意：Console 层禁止用 spawn 执行同步 CLI 操作（如 card add / worker launch）。
 * 这些都应走 Service 层直接调 Domain。只有 pipeline supervisor 这种长跑 + detach
 * 的进程才合法用 spawn。
 */
import { spawn } from 'node:child_process';
import { existsSync, mkdirSync, openSync } from 'node:fs';
import { basename, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
/**
 * 定位 CLI 主入口。优先用运行时的 process.argv[1]（作为 sps CLI 运行时就是 sps 可执行文件）；
 * 但当 SPS 被当作库嵌入到别的进程时（如 game-platform，process.argv[1] 是宿主入口），
 * 决不能 self-re-exec 宿主 —— 此时回退到由本模块位置算出的 sps 自身入口。
 */
function cliEntry() {
    const node = process.argv[0] ?? 'node';
    const argvEntry = process.argv[1];
    if (argvEntry && looksLikeSpsEntry(argvEntry))
        return { node, entry: argvEntry };
    return { node, entry: defaultEntry() };
}
function looksLikeSpsEntry(p) {
    const base = basename(p);
    return (base === 'main.js' ||
        base === 'main.ts' ||
        base === 'sps' ||
        p.includes('sps-cli') ||
        p.includes('workflow-cli'));
}
function defaultEntry() {
    // infra/spawn.ts → src/ → workflow-cli/ → main.ts (源码模式) 或 dist/main.js（编译）
    const here = fileURLToPath(import.meta.url);
    // 源码：.../src/infra/spawn.ts → .../src/main.ts
    const srcSibling = resolve(dirname(here), '..', 'main.ts');
    if (existsSync(srcSibling))
        return srcSibling;
    // 编译：.../dist/infra/spawn.js → .../dist/main.js
    const distSibling = resolve(dirname(here), '..', 'main.js');
    return distSibling;
}
export class NodeProcessSpawner {
    spawnSupervisor(opts) {
        const { node, entry } = cliEntry();
        const logDir = dirname(opts.logPath);
        if (!existsSync(logDir))
            mkdirSync(logDir, { recursive: true });
        const logFd = openSync(opts.logPath, 'a');
        const child = spawn(node, [entry, ...opts.args], {
            cwd: opts.cwd,
            detached: true,
            stdio: ['ignore', logFd, logFd],
        });
        child.unref();
        return child;
    }
    runCliSync(opts) {
        const { node, entry } = cliEntry();
        return runCollected(node, [entry, ...opts.args], opts.cwd, opts.timeoutMs);
    }
    runCommand(opts) {
        return runCollected(opts.command, opts.args, opts.cwd, opts.timeoutMs);
    }
}
// v0.50.17：runCliSync / runCommand 共用的收集器
function runCollected(command, args, cwd, timeoutMs) {
    return new Promise((resolvePromise) => {
        const child = spawn(command, args, {
            cwd,
            stdio: ['ignore', 'pipe', 'pipe'],
        });
        let stdout = '';
        let stderr = '';
        child.stdout?.on('data', (d) => {
            stdout += d.toString();
        });
        child.stderr?.on('data', (d) => {
            stderr += d.toString();
        });
        const timer = timeoutMs ? setTimeout(() => child.kill('SIGTERM'), timeoutMs) : null;
        child.on('close', (code) => {
            if (timer)
                clearTimeout(timer);
            resolvePromise({ exitCode: code, stdout, stderr });
        });
        child.on('error', (err) => {
            if (timer)
                clearTimeout(timer);
            resolvePromise({ exitCode: -1, stdout, stderr: stderr + String(err) });
        });
    });
}
/**
 * 不真 spawn，仅记录调用。用于 Service 层单测验证 "PipelineService.start
 * 的确调用了 supervisor"。
 */
export class FakeProcessSpawner {
    calls = [];
    syncCalls = [];
    commandCalls = [];
    /** 测试可以预设每次 runCliSync 的响应；缺省返 { exitCode: 0, stdout:'', stderr:'' } */
    nextSyncResult = null;
    /** v0.50.17：runCommand 的响应——key = `<command> <args[0]>` 头两段，值 = 预设响应 */
    commandResults = new Map();
    _pid = 100000;
    async runCliSync(opts) {
        this.syncCalls.push({ args: opts.args, cwd: opts.cwd });
        return this.nextSyncResult ?? { exitCode: 0, stdout: '', stderr: '' };
    }
    async runCommand(opts) {
        this.commandCalls.push({ command: opts.command, args: opts.args, cwd: opts.cwd });
        const key = `${opts.command} ${opts.args[0] ?? ''}`;
        return this.commandResults.get(key) ?? { exitCode: 0, stdout: '', stderr: '' };
    }
    spawnSupervisor(opts) {
        this.calls.push({ args: opts.args, logPath: opts.logPath, cwd: opts.cwd });
        // 返回一个 stub 足够满足 ChildProcess 接口用到的字段
        const pid = this._pid++;
        const stub = {
            pid,
            unref() { },
            kill() {
                return true;
            },
            on() {
                return stub;
            },
            once() {
                return stub;
            },
            removeListener() {
                return stub;
            },
        };
        return stub;
    }
}
//# sourceMappingURL=spawn.js.map