import { spawn } from 'child_process';
import crossSpawn from 'cross-spawn';
import { sessionsService } from './modules/providers/services/sessions.service.js';
import { providerAuthService } from './modules/providers/services/provider-auth.service.js';
import { providerModelsService } from './modules/providers/services/provider-models.service.js';
import { notifyRunFailed, notifyRunStopped } from './services/notification-orchestrator.js';
import { createNormalizedMessage } from './shared/utils.js';
const spawnFunction = process.platform === 'win32' ? crossSpawn : spawn;
const activeOpenCodeProcesses = new Map();
function readOpenCodeSessionId(event) {
    if (!event || typeof event !== 'object') {
        return null;
    }
    return event.sessionID || event.sessionId || null;
}
async function spawnOpenCode(command, options = {}, ws) {
    return new Promise((resolve, reject) => {
        const { sessionId, projectPath, cwd, model, sessionSummary } = options;
        const workingDir = cwd || projectPath || process.cwd();
        const processKey = sessionId || Date.now().toString();
        let capturedSessionId = sessionId || null;
        let sessionCreatedSent = false;
        let stdoutLineBuffer = '';
        let terminalNotificationSent = false;
        let opencodeProcess = null;
        const notifyTerminalState = ({ code = null, error = null } = {}) => {
            if (terminalNotificationSent) {
                return;
            }
            terminalNotificationSent = true;
            const finalSessionId = capturedSessionId || sessionId || processKey;
            if (code === 0 && !error) {
                notifyRunStopped({
                    userId: ws?.userId || null,
                    provider: 'opencode',
                    sessionId: finalSessionId,
                    sessionName: sessionSummary,
                    stopReason: 'completed',
                });
                return;
            }
            notifyRunFailed({
                userId: ws?.userId || null,
                provider: 'opencode',
                sessionId: finalSessionId,
                sessionName: sessionSummary,
                error: error || `OpenCode CLI exited with code ${code}`,
            });
        };
        const registerSession = (nextSessionId) => {
            if (!nextSessionId || capturedSessionId === nextSessionId) {
                return;
            }
            capturedSessionId = nextSessionId;
            if (processKey !== capturedSessionId && opencodeProcess) {
                activeOpenCodeProcesses.delete(processKey);
                activeOpenCodeProcesses.set(capturedSessionId, opencodeProcess);
            }
            if (opencodeProcess) {
                opencodeProcess.sessionId = capturedSessionId;
            }
            if (ws.setSessionId && typeof ws.setSessionId === 'function') {
                ws.setSessionId(capturedSessionId);
            }
            if (!sessionId && !sessionCreatedSent) {
                sessionCreatedSent = true;
                ws.send(createNormalizedMessage({
                    kind: 'session_created',
                    newSessionId: capturedSessionId,
                    sessionId: capturedSessionId,
                    provider: 'opencode',
                }));
            }
        };
        const processOpenCodeOutputLine = (line) => {
            if (!line || !line.trim()) {
                return;
            }
            let response;
            try {
                response = JSON.parse(line);
            }
            catch {
                ws.send(createNormalizedMessage({
                    kind: 'stream_delta',
                    content: line,
                    sessionId: capturedSessionId || sessionId || null,
                    provider: 'opencode',
                }));
                return;
            }
            try {
                registerSession(readOpenCodeSessionId(response));
                const normalized = sessionsService.normalizeMessage('opencode', response, capturedSessionId || sessionId || null);
                for (const msg of normalized) {
                    ws.send(msg);
                }
            }
            catch (error) {
                const errorContent = error instanceof Error ? error.message : String(error);
                console.error('[OpenCode] Failed to process JSON output:', errorContent);
                ws.send(createNormalizedMessage({
                    kind: 'error',
                    content: errorContent,
                    sessionId: capturedSessionId || sessionId || null,
                    provider: 'opencode',
                }));
            }
        };
        void providerModelsService.resolveResumeModel('opencode', sessionId, model).then((resolvedModel) => {
            const args = ['run', '--format', 'json'];
            if (sessionId) {
                args.push('--session', sessionId);
            }
            if (resolvedModel) {
                args.push('--model', resolvedModel);
            }
            if (command && command.trim()) {
                args.push(command.trim());
            }
            opencodeProcess = spawnFunction('opencode', args, {
                cwd: workingDir,
                stdio: ['pipe', 'pipe', 'pipe'],
                env: { ...process.env },
            });
            activeOpenCodeProcesses.set(processKey, opencodeProcess);
            opencodeProcess.sessionId = processKey;
            opencodeProcess.stdin.end();
            opencodeProcess.stdout.on('data', (data) => {
                stdoutLineBuffer += data.toString();
                const completeLines = stdoutLineBuffer.split(/\r?\n/);
                stdoutLineBuffer = completeLines.pop() || '';
                completeLines.forEach((line) => {
                    processOpenCodeOutputLine(line.trim());
                });
            });
            opencodeProcess.stderr.on('data', (data) => {
                const stderrText = data.toString();
                if (!stderrText.trim()) {
                    return;
                }
                ws.send(createNormalizedMessage({
                    kind: 'error',
                    content: stderrText,
                    sessionId: capturedSessionId || sessionId || null,
                    provider: 'opencode',
                }));
            });
            opencodeProcess.on('close', async (code) => {
                const finalSessionId = capturedSessionId || sessionId || processKey;
                activeOpenCodeProcesses.delete(finalSessionId);
                activeOpenCodeProcesses.delete(processKey);
                if (stdoutLineBuffer.trim()) {
                    processOpenCodeOutputLine(stdoutLineBuffer.trim());
                    stdoutLineBuffer = '';
                }
                ws.send(createNormalizedMessage({
                    kind: 'complete',
                    exitCode: code,
                    isNewSession: !sessionId && !!command,
                    sessionId: finalSessionId,
                    provider: 'opencode',
                }));
                if (code === 0) {
                    notifyTerminalState({ code });
                    resolve();
                    return;
                }
                if (code === 127 || code === null) {
                    const installed = await providerAuthService.isProviderInstalled('opencode');
                    if (!installed) {
                        ws.send(createNormalizedMessage({
                            kind: 'error',
                            content: 'OpenCode CLI is not installed. Install it from https://opencode.ai/docs/',
                            sessionId: finalSessionId,
                            provider: 'opencode',
                        }));
                    }
                }
                notifyTerminalState({ code });
                reject(new Error(code === null ? 'OpenCode CLI process was terminated' : `OpenCode CLI exited with code ${code}`));
            });
            opencodeProcess.on('error', async (error) => {
                const finalSessionId = capturedSessionId || sessionId || processKey;
                activeOpenCodeProcesses.delete(finalSessionId);
                activeOpenCodeProcesses.delete(processKey);
                const installed = await providerAuthService.isProviderInstalled('opencode');
                const errorContent = !installed
                    ? 'OpenCode CLI is not installed. Install it from https://opencode.ai/docs/'
                    : error.message;
                ws.send(createNormalizedMessage({
                    kind: 'error',
                    content: errorContent,
                    sessionId: finalSessionId,
                    provider: 'opencode',
                }));
                notifyTerminalState({ error });
                reject(error);
            });
        }).catch(reject);
    });
}
function abortOpenCodeSession(sessionId) {
    const process = activeOpenCodeProcesses.get(sessionId);
    if (!process) {
        return false;
    }
    process.kill('SIGTERM');
    activeOpenCodeProcesses.delete(sessionId);
    return true;
}
function isOpenCodeSessionActive(sessionId) {
    return activeOpenCodeProcesses.has(sessionId);
}
function getActiveOpenCodeSessions() {
    return Array.from(activeOpenCodeProcesses.keys());
}
export { spawnOpenCode, abortOpenCodeSession, isOpenCodeSessionActive, getActiveOpenCodeSessions, };
//# sourceMappingURL=opencode-cli.js.map