import { Database } from 'bun:sqlite';
import { createSubcommand } from '../../../types';
import * as tui from '../../../tui';
import { getCommand } from '../../../command-prefix';
import { isJSONMode, outputJSON } from '../../../output';
import { z } from 'zod';
import { REQUIRED_TABLES, isMemoryPath, getDefaultDBCandidates, resolveOpenCodeDBPath, parseDisplayTitle, } from './db';
const InspectArgsSchema = z.object({
    session: z.string().describe('Session ID to inspect'),
});
const InspectOptionsSchema = z.object({
    json: z.boolean().optional().describe('Output JSON format'),
});
function toISO(epochMs) {
    return new Date(epochMs).toISOString();
}
function computeSessionStatus(session, latestError, activeToolCount) {
    if (session.time_archived)
        return 'archived';
    if (session.time_compacting)
        return 'compacting';
    if (latestError)
        return 'error';
    if (activeToolCount > 0)
        return 'active';
    return 'idle';
}
function formatRelativeTime(epochMs) {
    const now = Date.now();
    const diffMs = now - epochMs;
    if (diffMs < 60 * 1000)
        return 'just now';
    if (diffMs < 60 * 60 * 1000) {
        const minutes = Math.floor(diffMs / (60 * 1000));
        return `${minutes}m ago`;
    }
    if (diffMs < 24 * 60 * 60 * 1000) {
        const hours = Math.floor(diffMs / (60 * 60 * 1000));
        return `${hours}h ago`;
    }
    if (diffMs < 7 * 24 * 60 * 60 * 1000) {
        const days = Math.floor(diffMs / (24 * 60 * 60 * 1000));
        return `${days}d ago`;
    }
    const date = new Date(epochMs);
    const yyyy = date.getFullYear();
    const mm = String(date.getMonth() + 1).padStart(2, '0');
    const dd = String(date.getDate()).padStart(2, '0');
    return `${yyyy}-${mm}-${dd}`;
}
export const inspectSubcommand = createSubcommand({
    name: 'inspect',
    description: 'Inspect a specific session in detail',
    tags: ['read-only', 'fast'],
    schema: {
        args: InspectArgsSchema,
        options: InspectOptionsSchema,
    },
    examples: [
        {
            command: getCommand('ai opencode inspect ses_abc123'),
            description: 'Inspect a specific session in detail',
        },
        {
            command: getCommand('ai opencode inspect ses_abc123 --json'),
            description: 'Inspect session as JSON',
        },
    ],
    async handler(ctx) {
        const { options, opts, args } = ctx;
        const jsonMode = isJSONMode(options) || opts?.json === true;
        const sessionId = args.session;
        const resolvedDbPath = await resolveOpenCodeDBPath();
        if (!resolvedDbPath) {
            const candidates = getDefaultDBCandidates();
            if (jsonMode) {
                outputJSON({
                    error: 'db_not_found',
                    message: 'OpenCode database not found.',
                    expectedLocations: candidates,
                });
            }
            else {
                tui.newline();
                tui.output(`${tui.ICONS.info} OpenCode database not found.`);
                tui.newline();
                tui.output('The inspect command requires OpenCode v1.2.0+ with SQLite storage.');
                if (candidates.length > 0) {
                    tui.output('Expected locations:');
                    for (const path of candidates) {
                        tui.output(`  ${tui.ICONS.bullet} ${path}`);
                    }
                }
                tui.newline();
                tui.output('Tip: Set OPENCODE_DB_PATH to override the database location.');
            }
            return { success: false };
        }
        const isMemory = isMemoryPath(resolvedDbPath);
        let db = null;
        try {
            db = new Database(resolvedDbPath, isMemory ? undefined : { readonly: true });
            // Validate schema
            const tableRows = db
                .query("SELECT name FROM sqlite_master WHERE type = 'table'")
                .all();
            const foundTables = new Set(tableRows.map((row) => row.name));
            for (const table of REQUIRED_TABLES) {
                if (!foundTables.has(table)) {
                    const msg = 'OpenCode database schema is missing required tables.';
                    if (jsonMode) {
                        outputJSON({ error: 'schema', message: msg });
                    }
                    else {
                        tui.error(msg);
                    }
                    return { success: false };
                }
            }
            // Query session
            const session = db
                .query(`SELECT id, project_id, parent_id, title, time_created, time_updated, time_compacting, time_archived
					 FROM session WHERE id = ?`)
                .get(sessionId);
            if (!session) {
                const msg = `Session ${sessionId} not found in database.`;
                if (jsonMode) {
                    outputJSON({ error: 'not_found', message: msg });
                }
                else {
                    tui.error(msg);
                }
                return { success: false };
            }
            // Query messages
            const messages = db
                .query(`SELECT m.id as id,
							m.session_id as session_id,
							CASE WHEN json_valid(m.data) THEN json_extract(m.data, '$.role') END as role,
							m.time_created as time_created,
							CASE WHEN json_valid(m.data) THEN json_extract(m.data, '$.agent') END as agent,
							CASE WHEN json_valid(m.data) THEN json_extract(m.data, '$.model') END as model,
							CASE WHEN json_valid(m.data) THEN json_extract(m.data, '$.cost') END as cost,
							CASE WHEN json_valid(m.data) THEN json_extract(m.data, '$.error') END as error
					 FROM message m
					 WHERE m.session_id = ?
					 ORDER BY m.time_created ASC`)
                .all(sessionId);
            // Get latest error for status computation
            const latestError = messages.length > 0 ? (messages[messages.length - 1]?.error ?? null) : null;
            // Query active tools
            const activeToolRows = db
                .query(`SELECT json_extract(p.data, '$.tool') as tool,
							json_extract(p.data, '$.state.status') as status,
							COALESCE(json_extract(p.data, '$.callID'), json_extract(p.data, '$.callId')) as call_id
					 FROM part p
					 WHERE p.session_id = ?
						AND json_valid(p.data)
						AND json_extract(p.data, '$.type') IN ('tool-invocation', 'tool')
						AND json_extract(p.data, '$.state.status') IN ('running', 'pending')`)
                .all(sessionId);
            const activeTools = activeToolRows.map((row) => ({
                tool: row.tool ?? 'unknown',
                status: row.status ?? 'unknown',
                callId: row.call_id ?? null,
            }));
            // Query recent tool history (last 20 completed tools)
            const recentToolRows = db
                .query(`SELECT json_extract(p.data, '$.tool') as tool,
							json_extract(p.data, '$.state.status') as status,
							COALESCE(json_extract(p.data, '$.callID'), json_extract(p.data, '$.callId')) as call_id
					 FROM part p
					 WHERE p.session_id = ?
						AND json_valid(p.data)
						AND json_extract(p.data, '$.type') IN ('tool-invocation', 'tool')
						AND json_extract(p.data, '$.state.status') NOT IN ('running', 'pending')
					 ORDER BY p.time_created DESC
					 LIMIT 20`)
                .all(sessionId);
            const recentTools = recentToolRows.map((row) => ({
                tool: row.tool ?? 'unknown',
                status: row.status ?? 'unknown',
                callId: row.call_id ?? null,
            }));
            // Query todos
            const todoRows = db
                .query('SELECT content, status, priority FROM todo WHERE session_id = ? ORDER BY position')
                .all(sessionId);
            const todos = todoRows.map((row) => ({
                content: row.content,
                status: row.status,
                priority: row.priority ?? undefined,
            }));
            // Query cost
            const costRow = db
                .query(`SELECT SUM(json_extract(m.data, '$.cost')) as total_cost,
							SUM(
								COALESCE(json_extract(m.data, '$.tokens.input'), 0) +
								COALESCE(json_extract(m.data, '$.tokens.output'), 0)
							) as total_tokens,
							SUM(COALESCE(json_extract(m.data, '$.tokens.input'), 0)) as input_tokens,
							SUM(COALESCE(json_extract(m.data, '$.tokens.output'), 0)) as output_tokens
					 FROM message m
					 WHERE m.session_id = ?
						AND json_valid(m.data)
						AND json_extract(m.data, '$.cost') IS NOT NULL`)
                .get(sessionId);
            const cost = {
                totalCost: costRow?.total_cost ?? 0,
                totalTokens: costRow?.total_tokens ?? 0,
                inputTokens: costRow?.input_tokens ?? 0,
                outputTokens: costRow?.output_tokens ?? 0,
            };
            // Query child sessions
            const childRows = db
                .query(`SELECT id, title, parent_id, time_created, time_updated, time_compacting, time_archived
					 FROM session
					 WHERE parent_id = ?
					 ORDER BY time_updated DESC`)
                .all(sessionId);
            const status = computeSessionStatus(session, latestError, activeTools.length);
            const displayTitle = parseDisplayTitle(session.title);
            // Build result
            const result = {
                session: {
                    id: session.id,
                    title: session.title,
                    displayTitle,
                    status,
                    projectId: session.project_id,
                    parentId: session.parent_id,
                    timeCreated: session.time_created,
                    timeUpdated: session.time_updated,
                    timeCreatedISO: toISO(session.time_created),
                    timeUpdatedISO: toISO(session.time_updated),
                },
                cost,
                messages: messages.map((m) => ({
                    id: m.id,
                    role: m.role ?? 'unknown',
                    agent: m.agent ?? null,
                    model: m.model ?? null,
                    cost: m.cost ?? null,
                    error: m.error ?? null,
                    timeCreated: m.time_created,
                    timeCreatedISO: toISO(m.time_created),
                })),
                activeTools,
                recentTools,
                todos,
                children: childRows.map((child) => {
                    const childDisplayTitle = parseDisplayTitle(child.title);
                    let childStatus = 'idle';
                    if (child.time_archived)
                        childStatus = 'archived';
                    else if (child.time_compacting)
                        childStatus = 'compacting';
                    return {
                        id: child.id,
                        title: child.title,
                        displayTitle: childDisplayTitle,
                        status: childStatus,
                        timeCreated: child.time_created,
                        timeUpdated: child.time_updated,
                        timeCreatedISO: toISO(child.time_created),
                        timeUpdatedISO: toISO(child.time_updated),
                    };
                }),
            };
            if (jsonMode) {
                outputJSON(result);
                return { success: true };
            }
            // Human-readable output
            tui.newline();
            tui.output(tui.bold('Session Info'));
            tui.output('────────────');
            tui.table([
                {
                    Field: 'ID',
                    Value: result.session.id,
                },
                {
                    Field: 'Title',
                    Value: result.session.displayTitle || result.session.title,
                },
                {
                    Field: 'Status',
                    Value: result.session.status,
                },
                {
                    Field: 'Project',
                    Value: result.session.projectId,
                },
                {
                    Field: 'Parent',
                    Value: result.session.parentId ?? '(root)',
                },
                {
                    Field: 'Created',
                    Value: `${result.session.timeCreatedISO} (${formatRelativeTime(result.session.timeCreated)})`,
                },
                {
                    Field: 'Updated',
                    Value: `${result.session.timeUpdatedISO} (${formatRelativeTime(result.session.timeUpdated)})`,
                },
            ], ['Field', 'Value']);
            tui.newline();
            tui.output(tui.bold('Cost Summary'));
            tui.output('────────────');
            tui.table([
                {
                    Metric: 'Total Cost',
                    Value: cost.totalCost > 0 ? `$${cost.totalCost.toFixed(4)}` : '$0.00',
                },
                { Metric: 'Total Tokens', Value: String(cost.totalTokens) },
                { Metric: 'Input Tokens', Value: String(cost.inputTokens) },
                { Metric: 'Output Tokens', Value: String(cost.outputTokens) },
            ], ['Metric', 'Value']);
            tui.newline();
            tui.output(tui.bold(`Messages (${messages.length})`));
            tui.output('────────────');
            if (messages.length === 0) {
                tui.output(tui.muted('No messages'));
            }
            else {
                const msgRows = result.messages.map((m) => ({
                    Role: m.role,
                    Agent: m.agent ?? '',
                    Model: m.model ?? '',
                    Cost: m.cost != null ? `$${m.cost.toFixed(4)}` : '',
                    Error: m.error ? 'yes' : '',
                    Time: formatRelativeTime(m.timeCreated),
                }));
                tui.table(msgRows, ['Role', 'Agent', 'Model', 'Cost', 'Error', 'Time']);
            }
            if (activeTools.length > 0) {
                tui.newline();
                tui.output(tui.bold(`Active Tools (${activeTools.length})`));
                tui.output('────────────');
                const toolRows = activeTools.map((t) => ({
                    Tool: t.tool,
                    Status: t.status,
                    'Call ID': t.callId ?? '',
                }));
                tui.table(toolRows, ['Tool', 'Status', 'Call ID']);
            }
            if (recentTools.length > 0) {
                tui.newline();
                tui.output(tui.bold(`Recent Tools (${recentTools.length})`));
                tui.output('────────────');
                const toolRows = recentTools.map((t) => ({
                    Tool: t.tool,
                    Status: t.status,
                    'Call ID': t.callId ?? '',
                }));
                tui.table(toolRows, ['Tool', 'Status', 'Call ID']);
            }
            tui.newline();
            tui.output(tui.bold(`Todos (${todos.length})`));
            tui.output('────────────');
            if (todos.length === 0) {
                tui.output(tui.muted('No todos'));
            }
            else {
                for (const todo of todos) {
                    const checked = todo.status === 'completed' ? 'x' : ' ';
                    const priorityTag = todo.priority ? ` [${todo.priority}]` : '';
                    tui.output(`- [${checked}]${priorityTag} ${todo.content}`);
                }
            }
            if (result.children.length > 0) {
                tui.newline();
                tui.output(tui.bold(`Children (${result.children.length})`));
                tui.output('────────────');
                const childTableRows = result.children.map((c) => ({
                    ID: c.id,
                    Title: c.displayTitle || c.title,
                    Status: c.status,
                    Updated: formatRelativeTime(c.timeUpdated),
                }));
                tui.table(childTableRows, ['ID', 'Title', 'Status', 'Updated']);
            }
            tui.newline();
            return { success: true };
        }
        catch (error) {
            const msg = error instanceof Error ? error.message : 'Failed to inspect session.';
            if (jsonMode) {
                outputJSON({ error: 'query_failed', message: msg });
            }
            else {
                tui.error(msg);
            }
            return { success: false };
        }
        finally {
            if (db) {
                db.close();
            }
        }
    },
});
export default inspectSubcommand;
//# sourceMappingURL=inspect.js.map