/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { Hono } from 'hono';
import { streamSSE } from 'hono/streaming';
import { detectRuntimes } from '../services/runtime-detector.js';
import { buildContext } from '../services/context-builder.js';
import { generateSessionId, spawnChat, getSession, getSessionHistory, touchSession, kill, } from '../services/chat-process-manager.js';
import { listConversations, loadConversation, saveConversation, deleteConversation, isValidConversationId, } from '../../services/chat-history.js';
import { getWorkspaceCwd } from '../middleware/workspace.js';
export function chatRoute(cwd) {
    const app = new Hono();
    // GET /api/v1/chat/runtimes — returns detected AI runtimes
    app.get('/runtimes', (c) => {
        const result = detectRuntimes();
        return c.json(result);
    });
    // POST /api/v1/chat/send — start or continue a chat session
    app.post('/send', async (c) => {
        const body = await c.req.json();
        if (!body.message || body.message.trim().length === 0) {
            return c.json({ error: 'Message is required' }, 400);
        }
        const workspace = body.workspace ?? getWorkspaceCwd(c, cwd);
        const runtime = body.runtime ?? 'claude';
        const sessionId = body.sessionId ?? generateSessionId();
        // Validate runtime is available
        const runtimes = detectRuntimes();
        const selectedRuntime = runtimes.runtimes.find((r) => r.id === runtime);
        if (!selectedRuntime?.available) {
            return c.json({ error: `Runtime "${runtime}" is not available` }, 400);
        }
        const context = buildContext(workspace);
        spawnChat(sessionId, body.message, workspace, runtime, context);
        return c.json({ sessionId });
    });
    // GET /api/v1/chat/stream?session=<id> — SSE stream for a chat session
    app.get('/stream', (c) => {
        const sessionId = c.req.query('session');
        if (!sessionId) {
            return c.json({ error: 'session query parameter is required' }, 400);
        }
        const session = getSession(sessionId);
        if (!session) {
            return c.json({ error: 'Session not found' }, 404);
        }
        // Process already finished before client connected — return buffered output immediately
        if (session.process.exitCode !== null) {
            const exitCode = session.process.exitCode;
            return streamSSE(c, async (stream) => {
                if (session.outputBuffer.length > 0) {
                    await stream.writeSSE({ data: JSON.stringify({ content: session.outputBuffer }), event: 'token' });
                }
                if (exitCode === 0) {
                    await stream.writeSSE({ data: JSON.stringify({ exitCode: 0 }), event: 'done' });
                }
                else {
                    await stream.writeSSE({
                        data: JSON.stringify({ message: `Process exited with code ${String(exitCode)}`, exitCode }),
                        event: 'error',
                    });
                }
            });
        }
        return streamSSE(c, async (stream) => {
            const proc = session.process;
            let closed = false;
            const safeWrite = async (event, data) => {
                if (closed)
                    return;
                try {
                    await stream.writeSSE({ data, event });
                }
                catch {
                    closed = true;
                }
            };
            const keepalive = setInterval(() => {
                touchSession(sessionId);
                void safeWrite('ping', '');
            }, 15_000);
            proc.stdout?.on('data', (chunk) => {
                touchSession(sessionId);
                const content = chunk.toString('utf8');
                void safeWrite('token', JSON.stringify({ content }));
            });
            let stderrBuffer = '';
            proc.stderr?.on('data', (chunk) => {
                stderrBuffer += chunk.toString('utf8');
            });
            // Wait for process to exit
            await new Promise((resolve) => {
                proc.on('close', (exitCode) => {
                    clearInterval(keepalive);
                    setTimeout(() => {
                        if (exitCode === 0) {
                            void safeWrite('done', JSON.stringify({ exitCode: 0 })).then(resolve);
                        }
                        else {
                            const errMsg = stderrBuffer.trim() || `Process exited with code ${String(exitCode)}`;
                            void safeWrite('error', JSON.stringify({ message: errMsg, exitCode })).then(resolve);
                        }
                    }, 100);
                });
                stream.onAbort(() => {
                    clearInterval(keepalive);
                    closed = true;
                    if (proc.exitCode === null) {
                        kill(sessionId);
                    }
                    resolve();
                });
            });
        });
    });
    // GET /api/v1/chat/session/:id/history — get conversation history for session
    app.get('/session/:id/history', (c) => {
        const sessionId = c.req.param('id');
        const history = getSessionHistory(sessionId);
        return c.json({ history });
    });
    // POST /api/v1/chat/end — end a chat session
    app.post('/end', async (c) => {
        const body = await c.req.json();
        if (!body.sessionId) {
            return c.json({ error: 'sessionId is required' }, 400);
        }
        const killed = kill(body.sessionId);
        if (!killed) {
            return c.json({ error: 'Session not found' }, 404);
        }
        return c.json({ ok: true });
    });
    // ─── Chat History Endpoints ──────────────────────────────────────────────
    // GET /api/v1/chat/history — list conversations (metadata only)
    app.get('/history', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const conversations = listConversations(dir);
        return c.json({ conversations });
    });
    // GET /api/v1/chat/history/:id — load full conversation with messages
    app.get('/history/:id', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const id = c.req.param('id');
        if (!isValidConversationId(id)) {
            return c.json({ error: 'Invalid conversation id' }, 400);
        }
        const conversation = loadConversation(dir, id);
        if (!conversation) {
            return c.json({ error: 'Conversation not found' }, 404);
        }
        return c.json(conversation);
    });
    // POST /api/v1/chat/history/:id/save — save or update a conversation
    app.post('/history/:id/save', async (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const id = c.req.param('id');
        if (!isValidConversationId(id)) {
            return c.json({ error: 'Invalid conversation id' }, 400);
        }
        const body = await c.req.json();
        if (!body.title || !body.messages) {
            return c.json({ error: 'title and messages are required' }, 400);
        }
        const conversation = saveConversation(dir, id, body.title, body.messages);
        return c.json(conversation);
    });
    // DELETE /api/v1/chat/history/:id — delete a conversation
    app.delete('/history/:id', (c) => {
        const dir = getWorkspaceCwd(c, cwd);
        const id = c.req.param('id');
        if (!isValidConversationId(id)) {
            return c.json({ error: 'Invalid conversation id' }, 400);
        }
        const deleted = deleteConversation(dir, id);
        if (!deleted) {
            return c.json({ error: 'Conversation not found' }, 404);
        }
        return c.json({ ok: true });
    });
    return app;
}
//# sourceMappingURL=chat.js.map