"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseCmdRunnerInitMessage = parseCmdRunnerInitMessage;
exports.parseCmdRunnerInitialIpcMessage = parseCmdRunnerInitialIpcMessage;
exports.parseCmdRunnerRequestLine = parseCmdRunnerRequestLine;
exports.parseCmdRunnerResponseLine = parseCmdRunnerResponseLine;
exports.getCmdRunnerEndpointForDaemonPid = getCmdRunnerEndpointForDaemonPid;
const node_os_1 = __importDefault(require("node:os"));
const node_path_1 = __importDefault(require("node:path"));
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function asString(value) {
    return typeof value === 'string' ? value : null;
}
function asNumber(value) {
    return typeof value === 'number' && Number.isFinite(value) ? value : null;
}
function asBoolean(value) {
    return typeof value === 'boolean' ? value : null;
}
function asNullableString(value) {
    if (value === undefined)
        return undefined;
    return value === null ? null : asString(value);
}
function parseStreamSnapshot(raw, label) {
    if (!isRecord(raw)) {
        throw new Error(`Invalid cmd_runner ${label}: expected object`);
    }
    const content = asString(raw['content']);
    const linesScrolledOut = asNumber(raw['linesScrolledOut']);
    if (content === null) {
        throw new Error(`Invalid cmd_runner ${label}.content: expected string`);
    }
    if (linesScrolledOut === null) {
        throw new Error(`Invalid cmd_runner ${label}.linesScrolledOut: expected number`);
    }
    return { content, linesScrolledOut };
}
function parseStatusPayload(raw) {
    const daemonPid = asNumber(raw['daemonPid']);
    const daemonCommandLine = asString(raw['daemonCommandLine']);
    const endpoint = asString(raw['endpoint']);
    const runnerPid = asNumber(raw['runnerPid']);
    const shell = asString(raw['shell']);
    const startTime = asString(raw['startTime']);
    const isRunning = asBoolean(raw['isRunning']);
    const exitCode = raw['exitCode'] === null
        ? null
        : raw['exitCode'] === undefined
            ? null
            : asNumber(raw['exitCode']);
    const exitSignal = asNullableString(raw['exitSignal']);
    if (daemonPid === null)
        throw new Error('Invalid cmd_runner payload.daemonPid');
    if (daemonCommandLine === null)
        throw new Error('Invalid cmd_runner payload.daemonCommandLine');
    if (endpoint === null)
        throw new Error('Invalid cmd_runner payload.endpoint');
    if (runnerPid === null)
        throw new Error('Invalid cmd_runner payload.runnerPid');
    if (shell === null)
        throw new Error('Invalid cmd_runner payload.shell');
    if (startTime === null)
        throw new Error('Invalid cmd_runner payload.startTime');
    if (isRunning === null)
        throw new Error('Invalid cmd_runner payload.isRunning');
    if (exitCode === null && raw['exitCode'] !== null && raw['exitCode'] !== undefined) {
        throw new Error('Invalid cmd_runner payload.exitCode');
    }
    if (exitSignal === undefined) {
        throw new Error('Invalid cmd_runner payload.exitSignal');
    }
    const processGroupId = raw['processGroupId'] === undefined ? undefined : (asNumber(raw['processGroupId']) ?? null);
    if (processGroupId === null) {
        throw new Error('Invalid cmd_runner payload.processGroupId');
    }
    return {
        daemonPid,
        daemonCommandLine,
        endpoint,
        runnerPid,
        processGroupId,
        shell,
        startTime,
        isRunning,
        exitCode,
        exitSignal,
        stdout: parseStreamSnapshot(raw['stdout'], 'payload.stdout'),
        stderr: parseStreamSnapshot(raw['stderr'], 'payload.stderr'),
    };
}
function parseCmdRunnerInitMessage(raw) {
    if (!isRecord(raw)) {
        throw new Error('Invalid cmd_runner init message: expected object');
    }
    if (raw['type'] !== 'init') {
        throw new Error('Invalid cmd_runner init message: expected type=init');
    }
    const initialCommandLine = asString(raw['initialCommandLine']);
    const timeoutSeconds = asNumber(raw['timeoutSeconds']);
    const scrollbackLines = asNumber(raw['scrollbackLines']);
    if (initialCommandLine === null || initialCommandLine.trim() === '') {
        throw new Error('Invalid cmd_runner init message: initialCommandLine required');
    }
    if (timeoutSeconds === null || !Number.isInteger(timeoutSeconds) || timeoutSeconds <= 0) {
        throw new Error('Invalid cmd_runner init message: timeoutSeconds must be positive integer');
    }
    if (scrollbackLines === null || !Number.isInteger(scrollbackLines) || scrollbackLines <= 0) {
        throw new Error('Invalid cmd_runner init message: scrollbackLines must be positive integer');
    }
    const spawnSpecRaw = raw['spawnSpec'];
    if (!isRecord(spawnSpecRaw)) {
        throw new Error('Invalid cmd_runner init message: spawnSpec required');
    }
    const command = asString(spawnSpecRaw['command']);
    const shellLabel = asString(spawnSpecRaw['shellLabel']);
    const windowsVerbatimArgumentsRaw = spawnSpecRaw['windowsVerbatimArguments'];
    const argsRaw = spawnSpecRaw['args'];
    if (command === null || command.trim() === '') {
        throw new Error('Invalid cmd_runner init message: spawnSpec.command required');
    }
    if (shellLabel === null || shellLabel.trim() === '') {
        throw new Error('Invalid cmd_runner init message: spawnSpec.shellLabel required');
    }
    if (windowsVerbatimArgumentsRaw !== undefined &&
        typeof windowsVerbatimArgumentsRaw !== 'boolean') {
        throw new Error('Invalid cmd_runner init message: spawnSpec.windowsVerbatimArguments must be boolean');
    }
    if (!Array.isArray(argsRaw) || !argsRaw.every((item) => typeof item === 'string')) {
        throw new Error('Invalid cmd_runner init message: spawnSpec.args must be string[]');
    }
    return {
        type: 'init',
        initialCommandLine,
        timeoutSeconds,
        scrollbackLines,
        spawnSpec: {
            command,
            args: [...argsRaw],
            shellLabel,
            ...(windowsVerbatimArgumentsRaw === true ? { windowsVerbatimArguments: true } : {}),
        },
    };
}
function parseCmdRunnerInitialIpcMessage(raw) {
    if (!isRecord(raw)) {
        throw new Error('Invalid cmd_runner IPC message: expected object');
    }
    const type = asString(raw['type']);
    if (type === 'completed') {
        const exitCode = raw['exitCode'] === null
            ? null
            : raw['exitCode'] === undefined
                ? null
                : asNumber(raw['exitCode']);
        const exitSignal = asNullableString(raw['exitSignal']);
        if (exitCode === null && raw['exitCode'] !== null && raw['exitCode'] !== undefined) {
            throw new Error('Invalid cmd_runner completed.exitCode');
        }
        if (exitSignal === undefined) {
            throw new Error('Invalid cmd_runner completed.exitSignal');
        }
        return {
            type,
            exitCode,
            exitSignal,
            stdout: parseStreamSnapshot(raw['stdout'], 'completed.stdout'),
            stderr: parseStreamSnapshot(raw['stderr'], 'completed.stderr'),
        };
    }
    if (type === 'daemonized') {
        const daemonPid = asNumber(raw['daemonPid']);
        const daemonCommandLine = asString(raw['daemonCommandLine']);
        const endpoint = asString(raw['endpoint']);
        const runnerPid = asNumber(raw['runnerPid']);
        const shell = asString(raw['shell']);
        const startTime = asString(raw['startTime']);
        const processGroupId = raw['processGroupId'] === undefined ? undefined : (asNumber(raw['processGroupId']) ?? null);
        if (daemonPid === null)
            throw new Error('Invalid cmd_runner daemonized.daemonPid');
        if (daemonCommandLine === null || daemonCommandLine.trim() === '') {
            throw new Error('Invalid cmd_runner daemonized.daemonCommandLine');
        }
        if (endpoint === null || endpoint.trim() === '') {
            throw new Error('Invalid cmd_runner daemonized.endpoint');
        }
        if (runnerPid === null)
            throw new Error('Invalid cmd_runner daemonized.runnerPid');
        if (shell === null || shell.trim() === '') {
            throw new Error('Invalid cmd_runner daemonized.shell');
        }
        if (startTime === null || startTime.trim() === '') {
            throw new Error('Invalid cmd_runner daemonized.startTime');
        }
        if (processGroupId === null) {
            throw new Error('Invalid cmd_runner daemonized.processGroupId');
        }
        return {
            type,
            daemonPid,
            daemonCommandLine,
            endpoint,
            runnerPid,
            processGroupId,
            shell,
            startTime,
        };
    }
    if (type === 'failed') {
        const errorText = asString(raw['errorText']);
        if (errorText === null || errorText.trim() === '') {
            throw new Error('Invalid cmd_runner failed.errorText');
        }
        return { type, errorText };
    }
    throw new Error(`Invalid cmd_runner IPC message type: ${String(type)}`);
}
function parseCmdRunnerRequestLine(line) {
    const raw = JSON.parse(line);
    if (!isRecord(raw)) {
        throw new Error('Invalid cmd_runner request: expected object');
    }
    const type = asString(raw['type']);
    if (type === 'ping' || type === 'get_status') {
        return { type };
    }
    if (type === 'stop') {
        const entirePg = asBoolean(raw['entirePg']);
        if (entirePg === null) {
            throw new Error('Invalid cmd_runner stop request: entirePg must be boolean');
        }
        return { type, entirePg };
    }
    if (type === 'get_output') {
        const stdout = asBoolean(raw['stdout']);
        const stderr = asBoolean(raw['stderr']);
        if (stdout === null || stderr === null) {
            throw new Error('Invalid cmd_runner get_output request: stdout/stderr must be boolean');
        }
        return { type, stdout, stderr };
    }
    throw new Error(`Invalid cmd_runner request type: ${String(type)}`);
}
function parseCmdRunnerResponseLine(line) {
    const raw = JSON.parse(line);
    if (!isRecord(raw)) {
        throw new Error('Invalid cmd_runner response: expected object');
    }
    const type = asString(raw['type']);
    const ok = asBoolean(raw['ok']);
    if (type === 'error') {
        const errorText = asString(raw['errorText']);
        if (ok !== false) {
            throw new Error('Invalid cmd_runner error response: ok must be false');
        }
        if (errorText === null || errorText.trim() === '') {
            throw new Error('Invalid cmd_runner error response: errorText required');
        }
        return { type, ok: false, errorText };
    }
    if (ok !== true) {
        throw new Error('Invalid cmd_runner response: ok must be true');
    }
    if (type !== 'pong' && type !== 'status' && type !== 'output' && type !== 'stop_result') {
        throw new Error(`Invalid cmd_runner response type: ${String(type)}`);
    }
    return {
        type,
        ok: true,
        ...parseStatusPayload(raw),
    };
}
function getCmdRunnerEndpointForDaemonPid(daemonPid) {
    if (!Number.isInteger(daemonPid) || daemonPid <= 0) {
        throw new Error(`Invalid daemon pid for cmd_runner endpoint: ${String(daemonPid)}`);
    }
    if (process.platform === 'win32') {
        return `\\\\.\\pipe\\dominds-cmd-${String(daemonPid)}`;
    }
    const baseDir = process.platform === 'darwin'
        ? node_os_1.default.tmpdir()
        : process.env['XDG_RUNTIME_DIR'] && process.env['XDG_RUNTIME_DIR']?.trim() !== ''
            ? process.env['XDG_RUNTIME_DIR']
            : node_os_1.default.tmpdir();
    return node_path_1.default.join(baseDir, 'dmcmd', `p-${String(daemonPid)}.sock`);
}
