"use strict";
/**
 * ChatProcessManager — manages claude --print SDK subprocess per project.
 *
 * Key design notes:
 * - sessionManager.startSession() is called AFTER system.init is received,
 *   not immediately after spawn (JSONL file doesn't exist yet at spawn time).
 * - tool_result blocks appear in USER-role messages in SDK output, not assistant.
 * - hasTerminal() is the public API name (mirrors TerminalManager.hasTerminal).
 * - Crash restart uses 3s delay; interrupt restart is immediate (500ms).
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.chatProcessManager = void 0;
const child_process_1 = require("child_process");
const events_1 = require("events");
const config_1 = require("./config");
const session_manager_1 = require("./session-manager");
class ChatProcessManager extends events_1.EventEmitter {
    constructor() {
        super(...arguments);
        this.instances = new Map();
        this.restartTimers = new Map();
    }
    start(project, continueSession = false) {
        this.stopInternal(project.id, false);
        const args = this.buildArgs(project, continueSession);
        const userShell = process.env.SHELL || '/bin/zsh';
        const proc = (0, child_process_1.spawn)(userShell, ['-ilc', args.join(' ')], {
            cwd: project.folderPath,
            env: { ...process.env },
            stdio: ['pipe', 'pipe', 'pipe'],
        });
        const instance = {
            process: proc,
            project,
            intentionalStop: false,
            lastActivityAt: null,
            sessionId: null,
            pendingToolName: null,
            lineBuffer: '',
        };
        this.instances.set(project.id, instance);
        project.status = 'running';
        (0, config_1.saveProject)(project);
        // NOTE: sessionManager.startSession() is called later, when system.init arrives
        proc.stdout?.on('data', (chunk) => {
            instance.lastActivityAt = Date.now();
            const text = chunk.toString('utf-8');
            // Auto-respond to Claude's workspace trust prompt (non-TTY: raw text on stdout)
            if (text.includes('Yes, I trust this folder') || text.includes('Quick safety check')) {
                try {
                    proc.stdin?.write('1\n');
                }
                catch { /**/ }
                return;
            }
            instance.lineBuffer += text;
            const lines = instance.lineBuffer.split('\n');
            instance.lineBuffer = lines.pop() ?? '';
            for (const line of lines) {
                const trimmed = line.trim();
                if (trimmed)
                    this.parseLine(project.id, instance, trimmed);
            }
        });
        proc.stderr?.on('data', (data) => {
            console.error(`[ChatProcess ${project.id}] stderr:`, data.toString().slice(0, 200));
        });
        proc.on('exit', (code) => {
            console.log(`[ChatProcess] Exit for ${project.id}, code: ${code}`);
            this.handleExit(project.id);
        });
        console.log(`[ChatProcessManager] Started for project ${project.id}`);
    }
    stop(projectId) {
        this.stopInternal(projectId, true);
    }
    /** Interrupt current generation and immediately restart with --continue */
    interrupt(projectId) {
        const instance = this.instances.get(projectId);
        if (!instance)
            return;
        // Mark intentionalStop so handleExit doesn't trigger crash-restart
        instance.intentionalStop = true;
        try {
            instance.process.kill('SIGINT');
        }
        catch { /**/ }
        this.instances.delete(projectId);
        this.emit('event', { projectId, type: 'status', status: 'restarting' });
        // Immediate restart (no 3s delay — user-initiated, not a crash)
        setTimeout(() => {
            this.start(instance.project, true);
        }, 500);
    }
    sendMessage(projectId, text) {
        const instance = this.instances.get(projectId);
        if (!instance?.process.stdin)
            return false;
        const msg = JSON.stringify({
            type: 'user',
            message: { role: 'user', content: text },
        }) + '\n';
        try {
            instance.process.stdin.write(msg);
            return true;
        }
        catch {
            return false;
        }
    }
    /** Named hasTerminal for API consistency with TerminalManager */
    hasTerminal(projectId) {
        return this.instances.has(projectId);
    }
    /** Stop all running chat processes — called on server shutdown. */
    stopAll() {
        for (const projectId of [...this.instances.keys()]) {
            this.stopInternal(projectId, false);
        }
        for (const timer of this.restartTimers.values())
            clearTimeout(timer);
        this.restartTimers.clear();
    }
    getLastActivityAt(projectId) {
        return this.instances.get(projectId)?.lastActivityAt ?? null;
    }
    stopInternal(projectId, updateStatus) {
        const timer = this.restartTimers.get(projectId);
        if (timer) {
            clearTimeout(timer);
            this.restartTimers.delete(projectId);
        }
        const instance = this.instances.get(projectId);
        if (!instance)
            return;
        instance.intentionalStop = true;
        try {
            instance.process.kill('SIGTERM');
        }
        catch { /**/ }
        this.instances.delete(projectId);
        session_manager_1.sessionManager.stopWatcherForProject(projectId);
        if (updateStatus) {
            instance.project.status = 'stopped';
            (0, config_1.saveProject)(instance.project);
        }
    }
    buildArgs(project, continueSession) {
        const args = [
            'claude',
            '--print',
            '--output-format', 'stream-json',
            '--input-format', 'stream-json',
            '--include-partial-messages',
            '--verbose',
        ];
        if (continueSession)
            args.push('--continue');
        if (project.permissionMode === 'unlimited')
            args.push('--dangerously-skip-permissions');
        return args;
    }
    parseLine(projectId, instance, line) {
        let record;
        try {
            record = JSON.parse(line);
        }
        catch {
            return;
        }
        const type = record.type;
        // ── system.init: call startSession now (JSONL file is about to be created) ──
        if (type === 'system' && record.subtype === 'init') {
            instance.sessionId = record.session_id ?? null;
            session_manager_1.sessionManager.startSession(instance.project.id, instance.project.folderPath);
            return;
        }
        // ── assistant messages: text, thinking, tool_use blocks ──
        if (type === 'assistant') {
            const msg = record.message;
            for (const block of (msg?.content ?? [])) {
                const btype = block.type;
                if (btype === 'text') {
                    const text = block.text ?? '';
                    if (text) {
                        this.emit('event', { projectId, type: 'stream', delta: text, contentType: 'text' });
                    }
                }
                else if (btype === 'thinking') {
                    const thinking = block.thinking ?? '';
                    if (thinking) {
                        this.emit('event', { projectId, type: 'stream', delta: thinking, contentType: 'thinking' });
                    }
                }
                else if (btype === 'tool_use') {
                    const name = block.name ?? 'tool';
                    instance.pendingToolName = name;
                    this.emit('event', { projectId, type: 'tool_start', toolName: name, toolInput: block.input });
                }
            }
            return;
        }
        // ── user messages: tool_result blocks (SDK returns tool results as user-role messages) ──
        if (type === 'user') {
            const msg = record.message;
            if (msg?.role !== 'tool')
                return; // only process tool result messages
            for (const block of (msg?.content ?? [])) {
                if (block.type === 'tool_result') {
                    const content = block.content;
                    const output = typeof content === 'string' ? content : JSON.stringify(content).slice(0, 500);
                    this.emit('event', {
                        projectId,
                        type: 'tool_end',
                        toolName: instance.pendingToolName ?? 'tool',
                        toolOutput: output,
                    });
                    instance.pendingToolName = null;
                }
            }
            return;
        }
        // ── result: turn complete ──
        if (type === 'result') {
            this.emit('event', { projectId, type: 'turn_end', costUsd: record.total_cost_usd ?? 0 });
            return;
        }
        // ── rate_limit_event ──
        if (type === 'rate_limit_event') {
            const info = record.rate_limit_info;
            if (info?.resetsAt) {
                this.emit('event', { projectId, type: 'rate_limit', resetsAt: info.resetsAt });
            }
        }
    }
    handleExit(projectId) {
        const instance = this.instances.get(projectId);
        if (!instance || instance.intentionalStop) {
            this.instances.delete(projectId);
            return;
        }
        const { project } = instance;
        this.instances.delete(projectId);
        project.status = 'restarting';
        (0, config_1.saveProject)(project);
        this.emit('event', { projectId, type: 'status', status: 'restarting' });
        console.log(`[ChatProcessManager] Auto-restarting ${projectId} with --continue in 3s...`);
        const timer = setTimeout(() => {
            this.restartTimers.delete(projectId);
            if (!this.instances.has(projectId))
                this.start(project, true);
        }, 3000);
        this.restartTimers.set(projectId, timer);
    }
}
exports.chatProcessManager = new ChatProcessManager();
//# sourceMappingURL=chat-process-manager.js.map