import { extractFileOpsFromBash } from './file_ops.js';
function extractErrorFromRawEvents(events, maxChars = 500) {
    const errorKeywords = ['error', 'Error', 'ERROR', 'failed', 'Failed', 'FAILED', 'exception', 'Exception'];
    for (let i = events.length - 1; i >= Math.max(0, events.length - 20); i--) {
        const event = events[i];
        if (event.type === 'raw') {
            const content = event.content || '';
            if (typeof content === 'string') {
                const contentLower = content.toLowerCase();
                if (errorKeywords.some(keyword => contentLower.includes(keyword.toLowerCase()))) {
                    let errorMsg = content.trim();
                    if (errorMsg.length > maxChars) {
                        errorMsg = errorMsg.substring(0, maxChars - 3) + '...';
                    }
                    return errorMsg;
                }
            }
        }
    }
    return null;
}
/** Event type priority tiers for filtering. Higher tiers surface more important events. */
export const PRIORITY = {
    critical: [
        'error',
        'result',
        'file_write',
        'file_delete',
        'file_create',
    ],
    important: [
        'tool_use',
        'bash',
        'file_read',
        'thinking',
        'message',
    ],
    verbose: [
        'thinking_delta',
        'message_delta',
        'init',
        'turn_start',
        'user_message',
        'raw',
    ],
};
/**
 * Collapse sequential events of the same type into summary entries.
 * Returns a cleaner list of events suitable for output.
 */
export function collapseEvents(events, maxEvents = 20) {
    if (events.length === 0)
        return [];
    const collapsed = [];
    let i = 0;
    while (i < events.length) {
        const event = events[i];
        const eventType = event.type || 'unknown';
        // For thinking events, collapse sequential ones
        if (eventType === 'thinking') {
            let count = 1;
            let lastContent = event.content || '';
            let j = i + 1;
            while (j < events.length && events[j].type === 'thinking') {
                count++;
                if (events[j].content) {
                    lastContent = events[j].content;
                }
                j++;
            }
            if (count > 1) {
                collapsed.push({
                    type: 'thinking_summary',
                    count: count,
                    last_content: lastContent.length > 200 ? lastContent.slice(-200) : lastContent,
                    timestamp: event.timestamp,
                });
            }
            else if (event.content) {
                collapsed.push(event);
            }
            i = j;
            continue;
        }
        // For message events, keep the last content
        if (eventType === 'message') {
            collapsed.push({
                type: 'message',
                content: event.content?.length > 500 ? event.content.slice(-500) : event.content,
                complete: event.complete,
                timestamp: event.timestamp,
            });
            i++;
            continue;
        }
        // Keep tool events as-is but truncate large content
        if (['bash', 'file_write', 'file_read', 'file_create', 'file_delete', 'tool_use'].includes(eventType)) {
            const cleaned = { ...event };
            if (cleaned.command && cleaned.command.length > 200) {
                cleaned.command = cleaned.command.slice(0, 200) + '...';
            }
            collapsed.push(cleaned);
            i++;
            continue;
        }
        // Keep errors and results
        if (['error', 'result'].includes(eventType)) {
            collapsed.push(event);
            i++;
            continue;
        }
        // Skip other event types
        i++;
    }
    // Return only the last N events
    if (collapsed.length > maxEvents) {
        return collapsed.slice(-maxEvents);
    }
    return collapsed;
}
/**
 * Get a breakdown of tool calls by type.
 */
export function getToolBreakdown(events) {
    const breakdown = {};
    for (const event of events) {
        const eventType = event.type || '';
        if (eventType === 'bash') {
            breakdown['bash'] = (breakdown['bash'] || 0) + 1;
        }
        else if (eventType === 'file_write') {
            breakdown['file_write'] = (breakdown['file_write'] || 0) + 1;
        }
        else if (eventType === 'file_read') {
            breakdown['file_read'] = (breakdown['file_read'] || 0) + 1;
        }
        else if (eventType === 'file_create') {
            breakdown['file_create'] = (breakdown['file_create'] || 0) + 1;
        }
        else if (eventType === 'file_delete') {
            breakdown['file_delete'] = (breakdown['file_delete'] || 0) + 1;
        }
        else if (eventType === 'tool_use') {
            const tool = event.tool || 'unknown';
            breakdown[tool] = (breakdown[tool] || 0) + 1;
        }
    }
    return breakdown;
}
/** Group consecutive events of the same type into combined entries with counts and truncated content. */
export function groupAndFlattenEvents(events) {
    if (events.length === 0)
        return [];
    const grouped = [];
    let i = 0;
    while (i < events.length) {
        const event = events[i];
        const eventType = event.type || 'unknown';
        if (eventType === 'message' || eventType === 'thinking') {
            let count = 1;
            let combinedContent = event.content || '';
            // Streaming token events (complete:false) get concatenated without a
            // separator so tokens reassemble into readable prose. Whole-turn events
            // get joined with newlines so distinct turns/thoughts stay separated.
            const isStreaming = event.complete === false;
            let j = i + 1;
            while (j < events.length && events[j].type === eventType) {
                count++;
                if (events[j].content) {
                    const sep = isStreaming || events[j].complete === false
                        ? ''
                        : (combinedContent ? '\n' : '');
                    combinedContent += sep + events[j].content;
                }
                j++;
            }
            const flattened = {
                type: eventType,
                content: combinedContent.length > 1000 ? combinedContent.slice(-1000) : combinedContent,
            };
            if (count > 1) {
                flattened.count = count;
            }
            grouped.push(flattened);
            i = j;
            continue;
        }
        if (['file_write', 'file_create', 'file_read', 'file_delete'].includes(eventType)) {
            const path = event.path || '';
            if (!path) {
                i++;
                continue;
            }
            const pathGroup = {
                type: eventType,
                path: path,
                count: 1,
            };
            let j = i + 1;
            while (j < events.length && events[j].type === eventType && events[j].path === path) {
                pathGroup.count++;
                j++;
            }
            grouped.push(pathGroup);
            i = j;
            continue;
        }
        if (eventType === 'bash') {
            const command = event.command || '';
            if (!command) {
                i++;
                continue;
            }
            const bashGroup = {
                type: 'bash',
                commands: [command],
                count: 1,
            };
            let j = i + 1;
            while (j < events.length && events[j].type === 'bash') {
                bashGroup.commands.push(events[j].command || '');
                bashGroup.count++;
                j++;
            }
            if (bashGroup.commands.length > 5) {
                bashGroup.commands = bashGroup.commands.slice(-5);
                bashGroup.truncated = bashGroup.count - 5;
            }
            grouped.push(bashGroup);
            i = j;
            continue;
        }
        if (eventType === 'tool_use') {
            const flattened = {
                type: 'tool_use',
                tool: event.tool || 'unknown',
            };
            if (event.name)
                flattened.name = event.name;
            if (event.input) {
                const inputStr = typeof event.input === 'string' ? event.input : JSON.stringify(event.input);
                flattened.input = inputStr.length > 200 ? inputStr.slice(0, 200) + '...' : inputStr;
            }
            grouped.push(flattened);
            i++;
            continue;
        }
        if (['error', 'result'].includes(eventType)) {
            const flattened = {
                type: eventType,
            };
            if (event.message)
                flattened.message = event.message;
            if (event.content)
                flattened.content = event.content;
            if (event.status)
                flattened.status = event.status;
            grouped.push(flattened);
            i++;
            continue;
        }
        i++;
    }
    return grouped;
}
/** Accumulated summary of an agent's activity: files touched, tools used, errors, and final message. */
export class AgentSummary {
    agentId;
    agentType;
    status;
    duration = null;
    filesModified = new Set();
    filesCreated = new Set();
    filesRead = new Set();
    filesDeleted = new Set();
    toolsUsed = new Set();
    toolCallCount = 0;
    bashCommands = [];
    errors = [];
    warnings = [];
    finalMessage = null;
    eventCount = 0;
    lastActivity = null;
    eventsCache = [];
    constructor(agentId, agentType, status, duration = null, eventCount = 0) {
        this.agentId = agentId;
        this.agentType = agentType;
        this.status = status;
        this.duration = duration;
        this.eventCount = eventCount;
    }
    toDict(detailLevel = 'standard') {
        const base = {
            agent_id: this.agentId,
            agent_type: this.agentType,
            status: this.status,
        };
        if (detailLevel === 'brief') {
            return {
                ...base,
                duration: this.duration,
                tool_call_count: this.toolCallCount,
                last_activity: this.lastActivity,
                files_modified: Array.from(this.filesModified).slice(0, 5),
                files_created: Array.from(this.filesCreated).slice(0, 5),
                has_errors: this.errors.length > 0,
            };
        }
        else if (detailLevel === 'standard') {
            return {
                ...base,
                duration: this.duration,
                files_modified: Array.from(this.filesModified),
                files_created: Array.from(this.filesCreated),
                tools_used: Array.from(this.toolsUsed),
                tool_call_count: this.toolCallCount,
                errors: this.errors.slice(0, 3),
                final_message: this.truncate(this.finalMessage, 2000),
            };
        }
        else {
            return {
                ...base,
                duration: this.duration,
                files_modified: Array.from(this.filesModified),
                files_created: Array.from(this.filesCreated),
                files_read: Array.from(this.filesRead),
                files_deleted: Array.from(this.filesDeleted),
                tools_used: Array.from(this.toolsUsed),
                tool_call_count: this.toolCallCount,
                bash_commands: this.bashCommands.slice(-10),
                errors: this.errors,
                warnings: this.warnings,
                final_message: this.finalMessage,
                event_count: this.eventCount,
                last_activity: this.lastActivity,
            };
        }
    }
    truncate(text, maxLen) {
        if (!text)
            return null;
        if (text.length <= maxLen)
            return text;
        return text.substring(0, maxLen - 3) + '...';
    }
}
/** Build an AgentSummary by walking all events and accumulating file ops, tool calls, errors, and messages. */
export function summarizeEvents(agentId, agentType, status, events, duration = null) {
    const summary = new AgentSummary(agentId, agentType, status, duration, events.length);
    summary.eventsCache = events;
    summary.agentId = agentId;
    summary.agentType = agentType;
    summary.status = status;
    for (const event of events) {
        const eventType = event.type || 'unknown';
        summary.lastActivity = eventType;
        if (eventType === 'file_write') {
            const path = event.path || '';
            if (path) {
                summary.filesModified.add(path);
                summary.toolCallCount++;
            }
        }
        else if (eventType === 'file_create') {
            const path = event.path || '';
            if (path) {
                summary.filesCreated.add(path);
                summary.toolCallCount++;
            }
        }
        else if (eventType === 'file_read') {
            const path = event.path || '';
            if (path) {
                summary.filesRead.add(path);
                summary.toolCallCount++;
            }
        }
        else if (eventType === 'file_delete') {
            const path = event.path || '';
            if (path) {
                summary.filesDeleted.add(path);
                summary.toolCallCount++;
            }
        }
        else if (eventType === 'directory_list') {
            summary.toolCallCount++;
        }
        else if (eventType === 'tool_use') {
            const tool = event.tool || 'unknown';
            summary.toolsUsed.add(tool);
            summary.toolCallCount++;
        }
        else if (eventType === 'bash') {
            const command = event.command || '';
            summary.toolsUsed.add('bash');
            if (command) {
                summary.bashCommands.push(command);
                const [filesRead, filesWritten, filesDeleted] = extractFileOpsFromBash(command);
                for (const path of filesRead) {
                    summary.filesRead.add(path);
                }
                for (const path of filesWritten) {
                    summary.filesModified.add(path);
                }
                for (const path of filesDeleted) {
                    summary.filesDeleted.add(path);
                }
            }
            summary.toolCallCount++;
        }
        else if (eventType === 'message') {
            const content = event.content || '';
            if (content) {
                // Streaming token-by-token messages (e.g., grok) arrive as many small
                // `message` events with complete:false; concatenate them so the final
                // turn reads as one message. Whole-turn messages (claude, codex,
                // gemini) keep their complete:true semantics — last one wins.
                if (event.complete === false) {
                    summary.finalMessage = (summary.finalMessage || '') + content;
                }
                else {
                    summary.finalMessage = content;
                }
            }
        }
        else if (eventType === 'error') {
            let errorMsg = null;
            for (const key of ['message', 'content', 'error', 'error_message', 'details']) {
                if (event[key]) {
                    errorMsg = String(event[key]);
                    break;
                }
            }
            if (!errorMsg) {
                errorMsg = extractErrorFromRawEvents(summary.eventsCache);
            }
            if (errorMsg) {
                if (errorMsg.length > 500) {
                    errorMsg = errorMsg.substring(0, 497) + '...';
                }
                summary.errors.push(errorMsg);
            }
        }
        else if (eventType === 'warning') {
            const warningMsg = event.message || event.content || '';
            if (warningMsg) {
                summary.warnings.push(warningMsg);
            }
        }
        else if (eventType === 'result') {
            if (event.status === 'error') {
                let errorMsg = null;
                for (const key of ['message', 'error', 'error_message', 'error_details', 'details']) {
                    if (event[key]) {
                        errorMsg = String(event[key]);
                        break;
                    }
                }
                if (!errorMsg) {
                    errorMsg = extractErrorFromRawEvents(summary.eventsCache);
                }
                if (errorMsg) {
                    if (errorMsg.length > 500) {
                        errorMsg = errorMsg.substring(0, 497) + '...';
                    }
                    summary.errors.push(errorMsg);
                }
            }
            if (!summary.duration && event.duration_ms) {
                const durationMs = event.duration_ms;
                const seconds = durationMs / 1000;
                if (seconds < 60) {
                    if (seconds % 1 === 0) {
                        summary.duration = `${Math.floor(seconds)} seconds`;
                    }
                    else {
                        summary.duration = `${seconds.toFixed(1)} seconds`;
                    }
                }
                else {
                    const minutes = seconds / 60;
                    summary.duration = `${minutes.toFixed(1)} minutes`;
                }
            }
        }
    }
    return summary;
}
/**
 * Compute the delta of new activity since a given point in time (ISO timestamp)
 * or event index. Used for incremental status polling.
 */
export function getDelta(agentId, agentType, status, events, since // Optional: ISO timestamp (string) or event index (number)
) {
    // Filter events by timestamp (string) or index (number)
    let newEvents;
    let sinceEvent = 0;
    if (since === undefined || since === null) {
        // No filter - return all events
        newEvents = events;
    }
    else if (typeof since === 'number') {
        // Backward compatibility: event index
        sinceEvent = since;
        newEvents = events.slice(sinceEvent);
    }
    else if (typeof since === 'string') {
        // New behavior: timestamp filtering
        const sinceDate = new Date(since);
        newEvents = events.filter((e) => {
            if (!e.timestamp)
                return false;
            const eventDate = new Date(e.timestamp);
            return eventDate > sinceDate;
        });
    }
    else {
        newEvents = events;
    }
    if (newEvents.length === 0) {
        return {
            agent_id: agentId,
            status: status,
            since_event: sinceEvent, // For backward compatibility
            new_events_count: 0,
            has_changes: false,
            new_files_created: [],
            new_files_modified: [],
            new_files_read: [],
            new_files_deleted: [],
            new_bash_commands: [],
            new_messages: [],
            new_tool_count: 0,
            new_errors: [],
        };
    }
    const summary = summarizeEvents(agentId, agentType, status, newEvents);
    return {
        agent_id: agentId,
        agent_type: agentType,
        status: status,
        since_event: sinceEvent, // For backward compatibility
        new_events_count: newEvents.length,
        current_event_count: sinceEvent + newEvents.length, // For backward compatibility
        has_changes: true,
        new_files_created: Array.from(summary.filesCreated),
        new_files_modified: Array.from(summary.filesModified),
        new_files_read: Array.from(summary.filesRead),
        new_files_deleted: Array.from(summary.filesDeleted),
        new_bash_commands: summary.bashCommands.slice(-15),
        new_messages: getLastMessages(newEvents, 5),
        new_tool_count: summary.toolCallCount,
        new_tool_calls: newEvents // For backward compatibility
            .filter((e) => ['tool_use', 'bash', 'file_write'].includes(e.type))
            .slice(-5)
            .map((e) => `${e.tool || 'unknown'}: ${e.command || e.path || ''}`),
        latest_message: summary.finalMessage, // For backward compatibility
        new_errors: summary.errors,
    };
}
/** Filter events to only those in the specified priority tiers (defaults to critical + important). */
export function filterEventsByPriority(events, includeLevels = null) {
    if (!includeLevels) {
        includeLevels = ['critical', 'important'];
    }
    const allowedTypes = new Set();
    for (const level of includeLevels) {
        const types = PRIORITY[level] || [];
        for (const type of types) {
            allowedTypes.add(type);
        }
    }
    return events.filter(e => allowedTypes.has(e.type));
}
/** Return the event type of the last tool-related or significant event, or null. */
export function getLastTool(events) {
    if (events.length === 0)
        return null;
    const lastEvent = events[events.length - 1];
    const eventType = lastEvent.type || '';
    const validTypes = ['tool_use', 'bash', 'file_write', 'file_create', 'file_read', 'file_delete', 'message', 'error', 'result'];
    if (validTypes.includes(eventType)) {
        return eventType;
    }
    return null;
}
/** Extract all tool_use events as {tool, args} pairs. */
export function getToolUses(events) {
    const toolUses = [];
    for (const event of events) {
        if (event.type === 'tool_use') {
            const tool = event.tool || 'unknown';
            const args = event.args || {};
            toolUses.push({ tool, args });
        }
    }
    return toolUses;
}
/** Extract the last N complete messages from the event stream, joining streaming deltas. */
export function getLastMessages(events, count = 3) {
    const messages = [];
    let currentBuffer = '';
    let isCollecting = false;
    for (const event of events) {
        if (event.type === 'message') {
            const content = event.content || '';
            // For streaming events (delta=true), content fragments should be joined.
            // We don't add newlines because these are likely parts of the same sentence/block.
            currentBuffer += content;
            isCollecting = true;
            // If we hit an explicitly complete message, treat it as a boundary
            if (event.complete) {
                if (currentBuffer.trim()) {
                    messages.push(currentBuffer);
                }
                currentBuffer = '';
                isCollecting = false;
            }
        }
        else {
            // Any non-message event breaks the message stream
            if (isCollecting) {
                if (currentBuffer.trim()) {
                    messages.push(currentBuffer);
                }
                currentBuffer = '';
                isCollecting = false;
            }
        }
    }
    // Handle any remaining buffer at the end
    if (isCollecting && currentBuffer.trim()) {
        messages.push(currentBuffer);
    }
    return messages.slice(-count);
}
/** Build a lightweight QuickStatus snapshot from agent events. */
export function getQuickStatus(agentId, agentType, status, events) {
    const filesCreatedSet = new Set();
    const filesModifiedSet = new Set();
    const filesDeletedSet = new Set();
    const filesReadSet = new Set();
    let toolCount = 0;
    let hasErrors = false;
    const commands = [];
    let lastMessage = null;
    for (const event of events) {
        const eventType = event.type || '';
        if (eventType === 'message') {
            const content = event.content || '';
            if (content) {
                lastMessage = content;
            }
        }
        else if (eventType === 'file_create') {
            const path = event.path || '';
            if (path) {
                filesCreatedSet.add(path);
            }
            toolCount++;
        }
        else if (eventType === 'file_write') {
            const path = event.path || '';
            if (path) {
                filesModifiedSet.add(path);
            }
            toolCount++;
        }
        else if (eventType === 'file_delete') {
            const path = event.path || '';
            if (path) {
                filesDeletedSet.add(path);
            }
            toolCount++;
        }
        else if (eventType === 'file_read') {
            const path = event.path || '';
            if (path) {
                filesReadSet.add(path);
            }
            toolCount++;
        }
        else if (eventType === 'bash') {
            toolCount++;
            const cmd = event.command || '';
            if (cmd) {
                commands.push(cmd.length > 100 ? cmd.substring(0, 97) + '...' : cmd);
                const [filesRead, filesWritten, filesDeleted] = extractFileOpsFromBash(cmd);
                for (const path of filesRead) {
                    filesReadSet.add(path);
                }
                for (const path of filesWritten) {
                    filesModifiedSet.add(path);
                }
                for (const path of filesDeleted) {
                    filesDeletedSet.add(path);
                }
            }
        }
        else if (eventType === 'directory_list') {
            toolCount++;
        }
        else if (['tool_use'].includes(eventType)) {
            toolCount++;
        }
        else if (eventType === 'error' || (eventType === 'result' && event.status === 'error')) {
            hasErrors = true;
        }
    }
    return {
        agent_id: agentId,
        agent_type: agentType,
        status: status,
        files_created: filesCreatedSet.size,
        files_modified: filesModifiedSet.size,
        files_deleted: filesDeletedSet.size,
        files_read: filesReadSet.size,
        tool_count: toolCount,
        last_commands: commands.slice(-3),
        has_errors: hasErrors,
        last_message: lastMessage,
    };
}
/** Produce a one-line human-readable status summary string (e.g. "Running, modified 3 files, used bash 5 times"). */
export function getStatusSummary(agentId, agentType, status, events, duration = null) {
    if (events.length === 0) {
        if (status === 'running') {
            return 'Just started, no activity yet';
        }
        return 'No activity';
    }
    let fileCount = 0;
    let bashCount = 0;
    let toolCount = 0;
    let hasErrors = false;
    for (const event of events) {
        const eventType = event.type || '';
        if (['file_write', 'file_create', 'file_delete'].includes(eventType)) {
            fileCount++;
        }
        else if (eventType === 'bash') {
            bashCount++;
        }
        else if (['tool_use', 'file_read'].includes(eventType)) {
            toolCount++;
        }
        else if (['error', 'result'].includes(eventType)) {
            if (event.status === 'error') {
                hasErrors = true;
            }
        }
    }
    const totalTools = bashCount + toolCount;
    const parts = [];
    if (status === 'running') {
        parts.push('Running');
    }
    else if (status === 'completed') {
        if (hasErrors) {
            parts.push('Completed with errors');
        }
        else {
            parts.push('Completed successfully');
        }
    }
    else if (status === 'failed') {
        parts.push('Failed');
    }
    else if (status === 'stopped') {
        parts.push('Stopped');
    }
    if (fileCount > 0) {
        parts.push(`modified ${fileCount} file${fileCount !== 1 ? 's' : ''}`);
    }
    if (bashCount > 0) {
        parts.push(`used bash ${bashCount} time${bashCount !== 1 ? 's' : ''}`);
    }
    if (toolCount > 0 && bashCount === 0) {
        parts.push(`used ${toolCount} tool${toolCount !== 1 ? 's' : ''}`);
    }
    if (totalTools > 0 && bashCount > 0) {
        parts.push(`used ${totalTools} tool${totalTools !== 1 ? 's' : ''}`);
    }
    if (hasErrors && status === 'running') {
        parts.push('has errors');
    }
    if (parts.length === 0) {
        if (status === 'running') {
            return `Running, ${events.length} event${events.length !== 1 ? 's' : ''} so far`;
        }
        return status.charAt(0).toUpperCase() + status.slice(1);
    }
    return parts.join(', ');
}
