"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.replay = replay;
exports.replayRecent = replayRecent;
exports.summarizeSession = summarizeSession;
exports.renderSessionSummary = renderSessionSummary;
const event_capture_1 = require("./event-capture");
const database_1 = require("../../06-memory/database/database");
function groupByLabel(events, by) {
    const groups = new Map();
    for (const ev of events) {
        let key;
        if (by === 'minute') {
            key = ev.createdAt.slice(0, 16); // YYYY-MM-DDTHH:MM
        }
        else if (by === 'hour') {
            key = ev.createdAt.slice(0, 13); // YYYY-MM-DDTHH
        }
        else if (by === 'file') {
            key = ev.filePath || '<no-file>';
        }
        else {
            key = ev.eventType;
        }
        if (!groups.has(key))
            groups.set(key, []);
        groups.get(key).push(ev);
    }
    return [...groups.entries()]
        .sort(([a], [b]) => a.localeCompare(b))
        .map(([label, evs]) => ({ label, events: evs }));
}
// ─── Event → Human-Readable Line ──────────────────────────────────────────
function eventToLine(ev, format) {
    const time = formatTimeForDisplay(ev.createdAt);
    const icon = eventIcon(ev.eventType);
    const status = ev.success === 0 ? ' ✗' : ev.success === 1 ? ' ✓' : '';
    switch (ev.eventType) {
        case 'file_save':
            return `${icon} ${time} save    ${ev.filePath || '?'}${status}`;
        case 'file_create':
            return `${icon} ${time} create  ${ev.filePath || '?'}${status}`;
        case 'file_delete':
            return `${icon} ${time} delete  ${ev.filePath || '?'}${status}`;
        case 'file_rename':
            return `${icon} ${time} rename  ${ev.filePath || '?'} → ${ev.metadata.newPath || '?'}${status}`;
        case 'cursor_move': {
            const sel = ev.metadata.selectionLength ? ` (sel: ${ev.metadata.selectionLength})` : '';
            return `${icon} ${time} cursor  ${ev.filePath || '?'}:${ev.line || '?'}:${ev.column || '?'}${sel}`;
        }
        case 'file_focus':
            return `${icon} ${time} focus   ${ev.filePath || '?'} ← ${ev.metadata.previousFile || '<start>'}`;
        case 'file_blur':
            return `${icon} ${time} blur    ${ev.filePath || '?'}`;
        case 'terminal_command':
            return `${icon} ${time} $       ${ev.command || '?'} (cwd: ${ev.filePath || '?'})`;
        case 'terminal_command_done': {
            const dur = ev.durationMs ? ` [${formatDuration(ev.durationMs)}]` : '';
            return `${icon} ${time} $ done  ${ev.command || '?'}${dur}${status}`;
        }
        case 'test_run':
            return `${icon} ${time} test    running…`;
        case 'test_pass':
            return `${icon} ${time} test ✓  ${ev.command || '?'} (${formatDuration(ev.durationMs || 0)})`;
        case 'test_fail':
            return `${icon} ${time} test ✗  ${ev.command || '?'} — ${ev.metadata.errorMessage || 'failed'}`;
        case 'build_start':
            return `${icon} ${time} build   ${ev.command || '?'} starting…`;
        case 'build_end':
            return `${icon} ${time} build   ${ev.command || '?'} ${ev.success === 1 ? 'done' : 'failed'} [${formatDuration(ev.durationMs || 0)}]${ev.metadata.errorCount ? ` (${ev.metadata.errorCount} errors)` : ''}`;
        case 'error':
            return `${icon} ${time} error   ${ev.command || '?'}: ${ev.metadata.errorMessage || '?'}${ev.filePath ? ` @ ${ev.filePath}:${ev.line || '?'}` : ''}`;
        case 'git_commit':
            return `${icon} ${time} commit  ${ev.metadata.commitHash || '?'} ${ev.command || '?'} (${ev.metadata.branch || '?'})`;
        case 'git_branch':
            return `${icon} ${time} branch  ${ev.command || '?'} → ${ev.filePath || '?'} (${ev.metadata.reason || 'switch'})`;
        case 'ai_prompt':
            return `${icon} ${time} prompt  ${ev.command || '?'}${ev.metadata.taskType ? ` [${ev.metadata.taskType}]` : ''} — "${truncate(String(ev.metadata.prompt || ''), 60)}"`;
        case 'ai_response':
            return `${icon} ${time} response ${ev.command || '?'}${status} [${formatDuration(ev.durationMs || 0)}]`;
        case 'session_start':
            return `${icon} ${time} session  START in ${ev.command || '?'}`;
        case 'session_end':
            return `${icon} ${time} session  END in ${ev.command || '?'}`;
        case 'package_change':
            return `${icon} ${time} pkg      ${ev.command || '?'} ${ev.metadata.oldVersion || '?'} → ${ev.metadata.newVersion || '?'}`;
        default:
            return `${icon} ${time} ${ev.eventType}  ${ev.filePath || ev.command || ''}`;
    }
}
function eventIcon(type) {
    const icons = {
        file_save: '💾', file_create: '📄', file_delete: '🗑', file_rename: '📝',
        cursor_move: '↳', file_focus: '👁', file_blur: '·',
        terminal_command: '$', terminal_command_done: '✓',
        test_run: '🧪', test_pass: '✓', test_fail: '✗',
        build_start: '🔨', build_end: '✓',
        error: '⚠',
        git_commit: '📌', git_branch: '⑂',
        ai_prompt: '💬', ai_response: '🤖',
        session_start: '▶', session_end: '⏹',
        package_change: '📦',
    };
    return icons[type] || '·';
}
function formatTimeForDisplay(iso) {
    if (!iso)
        return '??:??:??';
    try {
        const d = new Date(iso.endsWith('Z') || /[+-]\d{2}:?\d{2}$/.test(iso) ? iso : iso.replace(' ', 'T') + 'Z');
        if (isNaN(d.getTime()))
            return iso.slice(11, 19);
        const h = String(d.getHours()).padStart(2, '0');
        const m = String(d.getMinutes()).padStart(2, '0');
        const s = String(d.getSeconds()).padStart(2, '0');
        return `${h}:${m}:${s}`;
    }
    catch {
        return iso.slice(11, 19);
    }
}
function formatDuration(ms) {
    if (ms < 1000)
        return `${ms}ms`;
    if (ms < 60_000)
        return `${(ms / 1000).toFixed(1)}s`;
    const mins = Math.floor(ms / 60_000);
    const secs = Math.floor((ms % 60_000) / 1000);
    return `${mins}m${secs}s`;
}
function truncate(s, n) {
    if (s.length <= n)
        return s;
    return s.slice(0, n - 1) + '…';
}
// ─── Format Renderers ─────────────────────────────────────────────────────
function renderText(result, groupBy) {
    const lines = [];
    lines.push('━'.repeat(72));
    lines.push(`Session Replay — ${result.project}`);
    lines.push('━'.repeat(72));
    lines.push(`Total events: ${result.filteredCount}${result.filteredCount !== result.totalEvents ? ` (of ${result.totalEvents} total)` : ''}`);
    lines.push(`Time span: ${result.startTime} → ${result.endTime} (${result.durationMinutes} min)`);
    lines.push('');
    if (groupBy === 'none') {
        for (const ev of result.events) {
            lines.push(eventToLine(ev, 'text'));
        }
    }
    else {
        const groups = groupByLabel(result.events, groupBy);
        for (const g of groups) {
            lines.push('');
            lines.push(`── ${g.label} (${g.events.length} events) ──`);
            for (const ev of g.events) {
                lines.push('  ' + eventToLine(ev, 'text').trim());
            }
        }
    }
    lines.push('');
    lines.push('━'.repeat(72));
    lines.push('Event breakdown:');
    const sorted = Object.entries(result.eventsByType)
        .filter(([_, n]) => n > 0)
        .sort(([_, a], [__, b]) => b - a);
    for (const [type, n] of sorted) {
        lines.push(`  ${eventIcon(type)} ${type.padEnd(22)} ${n}`);
    }
    return lines.join('\n');
}
function renderCompact(result) {
    return result.events.map(e => {
        const t = formatTimeForDisplay(e.createdAt);
        const s = e.success === 0 ? '!' : e.success === 1 ? '✓' : '·';
        return `${t} ${s} ${e.eventType.padEnd(20)} ${e.filePath || e.command || ''}`;
    }).join('\n');
}
function renderMarkdown(result) {
    const lines = [];
    lines.push(`# Session Replay — ${result.project}\n`);
    lines.push(`**Time span:** ${result.startTime} → ${result.endTime} (${result.durationMinutes} min)  `);
    lines.push(`**Total events:** ${result.filteredCount}\n`);
    lines.push(`## Event Breakdown\n`);
    lines.push(`| Type | Count |`);
    lines.push(`|---|---:|`);
    const sorted = Object.entries(result.eventsByType)
        .filter(([_, n]) => n > 0)
        .sort(([_, a], [__, b]) => b - a);
    for (const [type, n] of sorted) {
        lines.push(`| ${type} | ${n} |`);
    }
    lines.push('');
    lines.push(`## Timeline\n`);
    lines.push('```');
    for (const ev of result.events) {
        lines.push(eventToLine(ev, 'text'));
    }
    lines.push('```');
    return lines.join('\n');
}
function renderJson(result) {
    return JSON.stringify(result, null, 2);
}
// ─── Main Replay Function ─────────────────────────────────────────────────
function replay(options) {
    (0, database_1.initializeDatabase)();
    // Get filtered events from the timeline
    const events = (0, event_capture_1.getTimeline)({
        project: options.project,
        sessionId: options.sessionId,
        eventTypes: options.eventTypes,
        since: options.since,
        until: options.until,
        filePath: options.filePath,
        filePattern: options.filePattern,
        limit: options.limit,
    });
    // Get total count of all events in project (for "filtered" indicator)
    const totalCounts = (0, event_capture_1.getEventCounts)(options.project);
    const totalEvents = Object.values(totalCounts).reduce((a, b) => a + b, 0);
    // Compute per-type counts for the filtered set
    const eventsByType = {};
    for (const t of Object.keys(totalCounts)) {
        eventsByType[t] = 0;
    }
    for (const ev of events) {
        eventsByType[ev.eventType] = (eventsByType[ev.eventType] || 0) + 1;
    }
    // Compute time span
    let startTime = '';
    let endTime = '';
    let durationMinutes = 0;
    if (events.length > 0) {
        startTime = events[0].createdAt;
        endTime = events[events.length - 1].createdAt;
        const start = new Date(startTime.endsWith('Z') ? startTime : startTime + 'Z').getTime();
        const end = new Date(endTime.endsWith('Z') ? endTime : endTime + 'Z').getTime();
        if (!isNaN(start) && !isNaN(end) && end > start) {
            durationMinutes = Math.round((end - start) / 60_000);
        }
    }
    // Render
    let formatted;
    if (options.format === 'json') {
        formatted = ''; // will be filled after we have full result
    }
    else if (options.format === 'markdown') {
        formatted = ''; // ditto
    }
    else if (options.format === 'compact') {
        formatted = ''; // ditto
    }
    else {
        formatted = ''; // ditto
    }
    const result = {
        project: options.project,
        totalEvents,
        filteredCount: events.length,
        startTime,
        endTime,
        durationMinutes,
        eventsByType,
        events,
        formatted,
    };
    // Now render with the complete result
    if (options.format === 'json') {
        result.formatted = renderJson(result);
    }
    else if (options.format === 'markdown') {
        result.formatted = renderMarkdown(result);
    }
    else if (options.format === 'compact') {
        result.formatted = renderCompact(result);
    }
    else {
        result.formatted = renderText(result, options.groupBy || 'none');
    }
    return result;
}
// ─── Convenience: Replay Last N Events ─────────────────────────────────────
function replayRecent(project, count, format = 'text') {
    const events = (0, event_capture_1.getMostRecentEvents)(project, count);
    // Reverse to chronological order
    events.reverse();
    const totalCounts = (0, event_capture_1.getEventCounts)(project);
    const totalEvents = Object.values(totalCounts).reduce((a, b) => a + b, 0);
    const eventsByType = {};
    for (const t of Object.keys(totalCounts)) {
        eventsByType[t] = 0;
    }
    for (const ev of events) {
        eventsByType[ev.eventType] = (eventsByType[ev.eventType] || 0) + 1;
    }
    let startTime = '';
    let endTime = '';
    let durationMinutes = 0;
    if (events.length > 0) {
        startTime = events[0].createdAt;
        endTime = events[events.length - 1].createdAt;
        const start = new Date(startTime.endsWith('Z') ? startTime : startTime + 'Z').getTime();
        const end = new Date(endTime.endsWith('Z') ? endTime : endTime + 'Z').getTime();
        if (!isNaN(start) && !isNaN(end) && end > start) {
            durationMinutes = Math.round((end - start) / 60_000);
        }
    }
    const result = {
        project,
        totalEvents,
        filteredCount: events.length,
        startTime,
        endTime,
        durationMinutes,
        eventsByType,
        events,
        formatted: '',
    };
    if (format === 'json')
        result.formatted = renderJson(result);
    else if (format === 'markdown')
        result.formatted = renderMarkdown(result);
    else if (format === 'compact')
        result.formatted = renderCompact(result);
    else
        result.formatted = renderText(result, 'none');
    return result;
}
function summarizeSession(project, sessionId) {
    (0, database_1.initializeDatabase)();
    const db = (0, database_1.getDb)();
    const row = db.prepare('SELECT id, tool_name, start_time, end_time, context_snapshot FROM sessions WHERE id = ? AND project = ?').get(sessionId, project);
    if (!row)
        return null;
    const events = (0, event_capture_1.getTimeline)({ project, sessionId });
    const counts = (0, event_capture_1.getEventCounts)(project);
    const sessionCounts = {};
    for (const t of Object.keys(counts)) {
        sessionCounts[t] = 0;
    }
    for (const ev of events) {
        sessionCounts[ev.eventType] = (sessionCounts[ev.eventType] || 0) + 1;
    }
    const files = new Set();
    const commands = [];
    let lastError = null;
    let lastFile = null;
    let aiAccepted = 0;
    let aiRejected = 0;
    for (const ev of events) {
        if (ev.filePath) {
            files.add(ev.filePath);
            if (ev.eventType === 'file_save' || ev.eventType === 'file_focus') {
                lastFile = ev.filePath;
            }
        }
        if (ev.command && (ev.eventType === 'terminal_command' || ev.eventType === 'terminal_command_done')) {
            commands.push(ev.command);
        }
        if (ev.eventType === 'error' && ev.metadata.errorMessage) {
            lastError = String(ev.metadata.errorMessage);
        }
        if (ev.eventType === 'ai_response') {
            if (ev.success === 1)
                aiAccepted++;
            else if (ev.success === 0)
                aiRejected++;
        }
    }
    const startTime = String(row.start_time || '');
    const endTime = row.end_time ? String(row.end_time) : null;
    let durationMinutes = 0;
    if (endTime) {
        const start = new Date(startTime.endsWith('Z') ? startTime : startTime + 'Z').getTime();
        const end = new Date(endTime.endsWith('Z') ? endTime : endTime + 'Z').getTime();
        if (!isNaN(start) && !isNaN(end) && end > start) {
            durationMinutes = Math.round((end - start) / 60_000);
        }
    }
    return {
        sessionId: Number(row.id),
        toolName: row.tool_name ? String(row.tool_name) : null,
        startTime,
        endTime,
        totalEvents: events.length,
        eventsByType: sessionCounts,
        uniqueFiles: [...files].sort(),
        uniqueCommands: commands,
        errors: sessionCounts.error || 0,
        testPasses: sessionCounts.test_pass || 0,
        testFails: sessionCounts.test_fail || 0,
        aiPrompts: sessionCounts.ai_prompt || 0,
        aiAccepted,
        aiRejected,
        lastErrorMessage: lastError,
        lastFileTouched: lastFile,
        durationMinutes,
        contextSnapshot: String(row.context_snapshot || ''),
    };
}
function renderSessionSummary(summary) {
    const lines = [];
    lines.push('━'.repeat(72));
    lines.push(`Session #${summary.sessionId} — ${summary.toolName || 'unknown tool'}`);
    lines.push('━'.repeat(72));
    lines.push(`Time:  ${summary.startTime} → ${summary.endTime || 'active'}${summary.durationMinutes ? ` (${summary.durationMinutes} min)` : ''}`);
    lines.push(`Events: ${summary.totalEvents} total`);
    lines.push(`Files:  ${summary.uniqueFiles.length} unique`);
    lines.push(`Commands: ${summary.uniqueCommands.length} terminal commands`);
    lines.push(`Errors: ${summary.errors}`);
    lines.push(`Tests:  ${summary.testPasses} pass, ${summary.testFails} fail`);
    lines.push(`AI:     ${summary.aiPrompts} prompts, ${summary.aiAccepted} accepted, ${summary.aiRejected} rejected`);
    if (summary.lastFileTouched) {
        lines.push(`Last file: ${summary.lastFileTouched}`);
    }
    if (summary.lastErrorMessage) {
        lines.push(`Last error: ${summary.lastErrorMessage}`);
    }
    if (summary.contextSnapshot) {
        lines.push('');
        lines.push(`Context snapshot:`);
        lines.push('  ' + summary.contextSnapshot);
    }
    return lines.join('\n');
}
//# sourceMappingURL=replay.js.map