/**
 * Session Content Parser - Parses native CLI tool session files
 * Supports Gemini/Qwen JSON and Codex JSONL formats
 */
import { readFileSync, existsSync } from 'fs';
/**
 * Detect if content is JSONL or JSON format
 */
function isJSONL(content) {
    const trimmed = content.trim();
    // JSON starts with { or [, but JSONL has multiple lines each starting with {
    if (trimmed.startsWith('['))
        return false; // JSON array
    if (!trimmed.startsWith('{'))
        return false;
    // Check if first line is complete JSON
    const firstLine = trimmed.split('\n')[0];
    try {
        JSON.parse(firstLine);
        // If multiple lines each parse as JSON, it's JSONL
        const lines = trimmed.split('\n').filter(l => l.trim());
        if (lines.length > 1) {
            // Try to parse second line
            JSON.parse(lines[1]);
            return true; // Multiple lines of JSON = JSONL
        }
        return false; // Single JSON object
    }
    catch {
        return false;
    }
}
/**
 * Parse a native session file and return standardized conversation data
 */
export function parseSessionFile(filePath, tool) {
    if (!existsSync(filePath)) {
        return null;
    }
    try {
        const content = readFileSync(filePath, 'utf8');
        switch (tool) {
            case 'gemini':
                return parseGeminiQwenSession(content, tool);
            case 'qwen':
                // Qwen can be either JSON (legacy) or JSONL (new format)
                if (isJSONL(content)) {
                    return parseQwenJSONLSession(content);
                }
                return parseGeminiQwenSession(content, tool);
            case 'codex':
                return parseCodexSession(content);
            default:
                return null;
        }
    }
    catch (error) {
        console.error(`Failed to parse session file ${filePath}:`, error);
        return null;
    }
}
/**
 * Parse Gemini or Qwen JSON session file
 */
function parseGeminiQwenSession(content, tool) {
    const session = JSON.parse(content);
    const turns = [];
    let turnNumber = 0;
    let totalTokens = { input: 0, output: 0, cached: 0, total: 0 };
    let model;
    for (const msg of session.messages) {
        if (msg.type === 'user') {
            turnNumber++;
            turns.push({
                turnNumber,
                timestamp: msg.timestamp,
                role: 'user',
                content: msg.content
            });
        }
        else if (msg.type === 'gemini' || msg.type === 'qwen') {
            // Find the corresponding user turn
            const userTurn = turns.find(t => t.turnNumber === turnNumber && t.role === 'user');
            // Extract thoughts
            const thoughts = msg.thoughts?.map(t => `${t.subject}: ${t.description}`) || [];
            turns.push({
                turnNumber,
                timestamp: msg.timestamp,
                role: 'assistant',
                content: msg.content,
                thoughts: thoughts.length > 0 ? thoughts : undefined,
                tokens: msg.tokens ? {
                    input: msg.tokens.input,
                    output: msg.tokens.output,
                    cached: msg.tokens.cached,
                    total: msg.tokens.total
                } : undefined
            });
            // Accumulate tokens
            if (msg.tokens) {
                totalTokens.input = (totalTokens.input || 0) + msg.tokens.input;
                totalTokens.output = (totalTokens.output || 0) + msg.tokens.output;
                totalTokens.cached = (totalTokens.cached || 0) + (msg.tokens.cached || 0);
                totalTokens.total = (totalTokens.total || 0) + msg.tokens.total;
            }
            if (msg.model) {
                model = msg.model;
            }
        }
    }
    return {
        sessionId: session.sessionId,
        tool,
        projectHash: session.projectHash,
        startTime: session.startTime,
        lastUpdated: session.lastUpdated,
        turns,
        totalTokens,
        model
    };
}
/**
 * Parse Qwen JSONL session file (new format)
 */
function parseQwenJSONLSession(content) {
    const lines = content.split('\n').filter(l => l.trim());
    const turns = [];
    let sessionId = '';
    let workingDir = '';
    let startTime = '';
    let lastUpdated = '';
    let model;
    let totalTokens = { input: 0, output: 0, cached: 0, total: 0 };
    let currentTurn = 0;
    for (const line of lines) {
        try {
            const entry = JSON.parse(line);
            lastUpdated = entry.timestamp;
            // Get session info from first entry
            if (!sessionId && entry.sessionId) {
                sessionId = entry.sessionId;
            }
            if (!workingDir && entry.cwd) {
                workingDir = entry.cwd;
            }
            if (!startTime) {
                startTime = entry.timestamp;
            }
            if (entry.type === 'user' && entry.message) {
                // User message
                currentTurn++;
                const textContent = entry.message.parts
                    .map(p => p.text || '')
                    .filter(t => t)
                    .join('\n');
                turns.push({
                    turnNumber: currentTurn,
                    timestamp: entry.timestamp,
                    role: 'user',
                    content: textContent
                });
            }
            else if (entry.type === 'assistant' && entry.message) {
                // Assistant response
                const textContent = entry.message.parts
                    .map(p => p.text || '')
                    .filter(t => t)
                    .join('\n');
                const tokens = entry.usageMetadata ? {
                    input: entry.usageMetadata.promptTokenCount,
                    output: entry.usageMetadata.candidatesTokenCount,
                    cached: entry.usageMetadata.cachedContentTokenCount || 0,
                    total: entry.usageMetadata.totalTokenCount
                } : undefined;
                turns.push({
                    turnNumber: currentTurn,
                    timestamp: entry.timestamp,
                    role: 'assistant',
                    content: textContent,
                    tokens
                });
                // Accumulate tokens
                if (tokens) {
                    totalTokens.input = (totalTokens.input || 0) + tokens.input;
                    totalTokens.output = (totalTokens.output || 0) + tokens.output;
                    totalTokens.cached = (totalTokens.cached || 0) + (tokens.cached || 0);
                    totalTokens.total = (totalTokens.total || 0) + tokens.total;
                }
                if (entry.model) {
                    model = entry.model;
                }
            }
            else if (entry.type === 'system' && entry.subtype === 'ui_telemetry') {
                // Telemetry event - extract model info if available
                if (entry.systemPayload?.uiEvent?.model && !model) {
                    model = entry.systemPayload.uiEvent.model;
                }
            }
        }
        catch {
            // Skip invalid lines
        }
    }
    return {
        sessionId,
        tool: 'qwen',
        workingDir,
        startTime,
        lastUpdated,
        turns,
        totalTokens,
        model
    };
}
/**
 * Parse Codex JSONL session file
 */
function parseCodexSession(content) {
    const lines = content.split('\n').filter(l => l.trim());
    const turns = [];
    let sessionId = '';
    let workingDir = '';
    let startTime = '';
    let lastUpdated = '';
    let model;
    let totalTokens = { input: 0, output: 0, total: 0 };
    let currentTurn = 0;
    let currentToolCalls = [];
    let pendingToolCalls = new Map();
    for (const line of lines) {
        try {
            const parsed = JSON.parse(line);
            lastUpdated = parsed.timestamp;
            if (parsed.type === 'session_meta') {
                const meta = parsed;
                sessionId = meta.payload.id;
                workingDir = meta.payload.cwd;
                startTime = meta.payload.timestamp;
            }
            else if (parsed.type === 'response_item') {
                const item = parsed;
                if (item.payload.type === 'message' && item.payload.role === 'user') {
                    // User message
                    currentTurn++;
                    const textContent = item.payload.content
                        ?.filter(c => c.type === 'input_text')
                        .map(c => c.text)
                        .join('\n') || '';
                    turns.push({
                        turnNumber: currentTurn,
                        timestamp: parsed.timestamp,
                        role: 'user',
                        content: textContent
                    });
                    // Reset tool calls for new turn
                    currentToolCalls = [];
                    pendingToolCalls.clear();
                }
                else if (item.payload.type === 'function_call') {
                    // Tool call
                    const toolCall = {
                        name: item.payload.name || 'unknown',
                        arguments: item.payload.arguments
                    };
                    if (item.payload.call_id) {
                        pendingToolCalls.set(item.payload.call_id, toolCall);
                    }
                    currentToolCalls.push(toolCall);
                }
                else if (item.payload.type === 'function_call_output') {
                    // Tool result
                    if (item.payload.call_id && pendingToolCalls.has(item.payload.call_id)) {
                        const toolCall = pendingToolCalls.get(item.payload.call_id);
                        toolCall.output = item.payload.output;
                    }
                }
                else if (item.payload.type === 'message' && item.payload.role === 'assistant') {
                    // Assistant message (final response)
                    const textContent = item.payload.content
                        ?.filter(c => c.type === 'output_text' || c.type === 'text')
                        .map(c => c.text)
                        .join('\n') || '';
                    if (textContent) {
                        turns.push({
                            turnNumber: currentTurn,
                            timestamp: parsed.timestamp,
                            role: 'assistant',
                            content: textContent,
                            toolCalls: currentToolCalls.length > 0 ? [...currentToolCalls] : undefined
                        });
                    }
                }
                else if (item.payload.type === 'reasoning') {
                    // Reasoning (may be encrypted, extract summary if available)
                    const summary = item.payload.summary;
                    if (summary && summary.length > 0) {
                        // Add reasoning summary to the last assistant turn
                        const lastAssistantTurn = turns.findLast(t => t.role === 'assistant');
                        if (lastAssistantTurn) {
                            lastAssistantTurn.thoughts = summary;
                        }
                    }
                }
            }
            else if (parsed.type === 'event_msg') {
                const event = parsed;
                if (event.payload.type === 'token_count' && event.payload.info?.total_token_usage) {
                    const usage = event.payload.info.total_token_usage;
                    totalTokens = {
                        input: usage.input_tokens,
                        output: usage.output_tokens,
                        total: usage.total_tokens
                    };
                }
            }
        }
        catch {
            // Skip invalid lines
        }
    }
    // If we have tool calls but no final assistant message, create one
    if (currentToolCalls.length > 0) {
        const lastTurn = turns[turns.length - 1];
        if (lastTurn && lastTurn.role === 'user') {
            // Find if there's already an assistant response for this turn
            const hasAssistant = turns.some(t => t.turnNumber === currentTurn && t.role === 'assistant');
            if (!hasAssistant) {
                turns.push({
                    turnNumber: currentTurn,
                    timestamp: lastUpdated,
                    role: 'assistant',
                    content: '[Tool execution completed]',
                    toolCalls: currentToolCalls
                });
            }
        }
    }
    return {
        sessionId,
        tool: 'codex',
        workingDir,
        startTime,
        lastUpdated,
        turns,
        totalTokens,
        model
    };
}
/**
 * Get conversation as formatted text
 */
export function formatConversation(session, options) {
    const { includeThoughts = false, includeToolCalls = false, includeTokens = false, maxContentLength = 4096 } = options || {};
    const lines = [];
    lines.push(`=== Session: ${session.sessionId} ===`);
    lines.push(`Tool: ${session.tool}`);
    lines.push(`Started: ${session.startTime}`);
    lines.push(`Updated: ${session.lastUpdated}`);
    if (session.model) {
        lines.push(`Model: ${session.model}`);
    }
    lines.push('');
    for (const turn of session.turns) {
        const roleLabel = turn.role === 'user' ? 'USER' : 'ASSISTANT';
        lines.push(`--- Turn ${turn.turnNumber} [${roleLabel}] ---`);
        const content = turn.content.length > maxContentLength
            ? turn.content.substring(0, maxContentLength) + '\n[truncated]'
            : turn.content;
        lines.push(content);
        if (includeThoughts && turn.thoughts && turn.thoughts.length > 0) {
            lines.push('');
            lines.push('Thoughts:');
            for (const thought of turn.thoughts) {
                lines.push(`  - ${thought}`);
            }
        }
        if (includeToolCalls && turn.toolCalls && turn.toolCalls.length > 0) {
            lines.push('');
            lines.push('Tool Calls:');
            for (const tc of turn.toolCalls) {
                lines.push(`  - ${tc.name}`);
                if (tc.output) {
                    const output = tc.output.length > 200
                        ? tc.output.substring(0, 200) + '...'
                        : tc.output;
                    lines.push(`    Output: ${output}`);
                }
            }
        }
        if (includeTokens && turn.tokens) {
            lines.push(`Tokens: ${turn.tokens.total} (in: ${turn.tokens.input}, out: ${turn.tokens.output})`);
        }
        lines.push('');
    }
    if (session.totalTokens) {
        lines.push(`=== Total Tokens: ${session.totalTokens.total} ===`);
    }
    return lines.join('\n');
}
/**
 * Extract just user prompts and assistant responses as simple pairs
 */
export function extractConversationPairs(session) {
    const pairs = [];
    const turnNumbers = [...new Set(session.turns.map(t => t.turnNumber))];
    for (const turnNum of turnNumbers) {
        const userTurn = session.turns.find(t => t.turnNumber === turnNum && t.role === 'user');
        const assistantTurn = session.turns.find(t => t.turnNumber === turnNum && t.role === 'assistant');
        if (userTurn) {
            pairs.push({
                turn: turnNum,
                userPrompt: userTurn.content,
                assistantResponse: assistantTurn?.content || '',
                timestamp: userTurn.timestamp
            });
        }
    }
    return pairs;
}
//# sourceMappingURL=session-content-parser.js.map