import { spawnSync } from 'node:child_process';
import { existsSync, readFileSync } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
const PROCESS_START_SKEW_SECONDS = 2;
let cachedClockTicksPerSecond = null;
export function resolveCodexTranscriptFromProcessLogs(pid, options = {}) {
    if (!Number.isInteger(pid) || pid <= 0) {
        return null;
    }
    const homeDir = options.homeDir ?? os.homedir();
    const codexDir = path.join(homeDir, '.codex');
    const logsDb = path.join(codexDir, 'logs_2.sqlite');
    const stateDb = path.join(codexDir, 'state_5.sqlite');
    if (!existsSync(logsDb) || !existsSync(stateDb)) {
        return null;
    }
    const processStartedAtSeconds = options.processStartedAtSeconds !== undefined
        ? options.processStartedAtSeconds
        : readProcessStartedAtSeconds(pid);
    if (processStartedAtSeconds === null) {
        return null;
    }
    const minLogSeconds = Math.max(0, Math.floor(processStartedAtSeconds) - PROCESS_START_SKEW_SECONDS);
    const output = (options.runSqlite ?? runSqlite)(logsDb, [
        `ATTACH DATABASE ${sqlString(stateDb)} AS state;`,
        "SELECT l.thread_id || char(9) || state.threads.rollout_path",
        'FROM logs l',
        'JOIN state.threads ON state.threads.id = l.thread_id',
        `WHERE l.process_uuid LIKE ${sqlString(`pid:${pid}:%`)}`,
        'AND l.thread_id IS NOT NULL',
        `AND l.ts >= ${minLogSeconds}`,
        "AND state.threads.source = 'cli'",
        "AND state.threads.model_provider = 'openai'",
        'AND state.threads.rollout_path IS NOT NULL',
        'ORDER BY l.ts DESC, l.ts_nanos DESC, l.id DESC',
        'LIMIT 50;'
    ].join('\n'));
    if (output === null) {
        return null;
    }
    const matches = new Map();
    for (const line of output.split('\n')) {
        const trimmed = line.trim();
        if (!trimmed)
            continue;
        const separator = trimmed.indexOf('\t');
        if (separator === -1)
            continue;
        const sessionId = trimmed.slice(0, separator);
        const filePath = trimmed.slice(separator + 1);
        if (!isSessionId(sessionId) || !isCodexRolloutPath(filePath, sessionId) || !existsSync(filePath)) {
            continue;
        }
        matches.set(sessionId, filePath);
    }
    if (matches.size !== 1) {
        return null;
    }
    const [entry] = matches.entries();
    if (!entry) {
        return null;
    }
    const [sessionId, filePath] = entry;
    return { provider: 'codex', sessionId, filePath };
}
function runSqlite(databasePath, sql) {
    const result = spawnSync('sqlite3', ['-batch', '-noheader', databasePath, sql], {
        stdio: ['ignore', 'pipe', 'pipe'],
        encoding: 'utf8',
        timeout: 1000
    });
    if (result.status !== 0) {
        return null;
    }
    return result.stdout;
}
function readProcessStartedAtSeconds(pid) {
    const bootTime = readBootTimeSeconds();
    const clockTicks = readClockTicksPerSecond();
    if (bootTime === null || clockTicks === null) {
        return null;
    }
    let raw;
    try {
        raw = readFileSync(`/proc/${pid}/stat`, 'utf8');
    }
    catch {
        return null;
    }
    const closeParen = raw.lastIndexOf(')');
    if (closeParen === -1) {
        return null;
    }
    const fieldsAfterCommand = raw.slice(closeParen + 2).trim().split(/\s+/);
    const startTicksRaw = fieldsAfterCommand[19];
    const startTicks = Number(startTicksRaw);
    if (!Number.isFinite(startTicks) || startTicks < 0) {
        return null;
    }
    return bootTime + startTicks / clockTicks;
}
function readBootTimeSeconds() {
    let raw;
    try {
        raw = readFileSync('/proc/stat', 'utf8');
    }
    catch {
        return null;
    }
    const line = raw.split('\n').find((entry) => entry.startsWith('btime '));
    if (!line) {
        return null;
    }
    const bootTime = Number(line.slice('btime '.length).trim());
    return Number.isFinite(bootTime) && bootTime > 0 ? bootTime : null;
}
function readClockTicksPerSecond() {
    if (cachedClockTicksPerSecond !== null) {
        return cachedClockTicksPerSecond;
    }
    const result = spawnSync('getconf', ['CLK_TCK'], {
        stdio: ['ignore', 'pipe', 'ignore'],
        encoding: 'utf8',
        timeout: 1000
    });
    const ticks = Number(result.stdout.trim());
    if (!Number.isFinite(ticks) || ticks <= 0) {
        return null;
    }
    cachedClockTicksPerSecond = ticks;
    return ticks;
}
function isCodexRolloutPath(filePath, sessionId) {
    const base = path.basename(filePath);
    return base.startsWith('rollout-') && base.endsWith(`${sessionId}.jsonl`);
}
function isSessionId(value) {
    return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(value);
}
function sqlString(value) {
    return `'${value.replace(/'/g, "''")}'`;
}
//# sourceMappingURL=codexTranscriptSource.js.map