/**
 * Interaction Logger
 *
 * Comprehensive session logging with replay capability (mistral-vibe style).
 * Saves complete interaction history including messages, tool calls, metadata.
 */
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync, unlinkSync } from 'fs';
import { join, dirname } from 'path';
import { homedir } from 'os';
import { randomUUID } from 'crypto';
// ============================================================================
// Interaction Logger
// ============================================================================
const LOG_DIR = join(homedir(), '.codebuddy', 'logs');
const LOG_VERSION = '1.0.0';
/**
 * Generate a short ID from UUID
 */
function generateShortId(uuid) {
    return uuid.substring(0, 8);
}
/**
 * Get log file path for a session
 */
function getLogPath(sessionId) {
    const date = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
    const dir = join(LOG_DIR, date);
    if (!existsSync(dir)) {
        mkdirSync(dir, { recursive: true });
    }
    return join(dir, `${sessionId}.json`);
}
/**
 * Interaction Logger for comprehensive session tracking
 */
export class InteractionLogger {
    currentSession = null;
    logPath = null;
    autoSave;
    saveInterval = null;
    constructor(options) {
        this.autoSave = options?.autoSave ?? true;
        // Auto-save every 30 seconds if enabled
        if (this.autoSave && options?.saveIntervalMs) {
            this.saveInterval = setInterval(() => {
                if (this.currentSession) {
                    this.save();
                }
            }, options.saveIntervalMs);
        }
    }
    /**
     * Start a new session
     */
    startSession(options) {
        const id = randomUUID();
        const shortId = generateShortId(id);
        this.currentSession = {
            version: LOG_VERSION,
            metadata: {
                id,
                short_id: shortId,
                started_at: new Date().toISOString(),
                model: options.model,
                provider: options.provider,
                cwd: options.cwd || process.cwd(),
                total_input_tokens: 0,
                total_output_tokens: 0,
                estimated_cost: 0,
                turns: 0,
                tool_calls: 0,
                tags: options.tags,
                description: options.description,
                git_branch: options.gitInfo?.branch,
                git_commit: options.gitInfo?.commit,
            },
            messages: [],
        };
        this.logPath = getLogPath(id);
        this.save();
        return id;
    }
    /**
     * End current session
     */
    endSession() {
        if (!this.currentSession)
            return;
        this.currentSession.metadata.ended_at = new Date().toISOString();
        this.currentSession.metadata.duration_ms =
            new Date().getTime() - new Date(this.currentSession.metadata.started_at).getTime();
        this.save();
        this.currentSession = null;
        this.logPath = null;
    }
    /**
     * Log a message
     */
    logMessage(message) {
        if (!this.currentSession)
            return;
        const logged = {
            role: message.role,
            content: message.content,
            timestamp: new Date().toISOString(),
            tool_call_id: message.tool_call_id,
            tokens: message.tokens,
        };
        this.currentSession.messages.push(logged);
        // Update metadata
        if (message.role === 'user') {
            this.currentSession.metadata.turns++;
        }
        if (message.tokens) {
            if (message.role === 'user' || message.role === 'system') {
                this.currentSession.metadata.total_input_tokens += message.tokens;
            }
            else {
                this.currentSession.metadata.total_output_tokens += message.tokens;
            }
        }
        if (this.autoSave) {
            this.save();
        }
    }
    /**
     * Log tool calls
     */
    logToolCalls(toolCalls) {
        if (!this.currentSession)
            return;
        // Find the last assistant message and add tool calls
        const lastAssistantIdx = this.currentSession.messages
            .map((m, i) => ({ m, i }))
            .filter(({ m }) => m.role === 'assistant')
            .pop()?.i;
        if (lastAssistantIdx !== undefined) {
            const msg = this.currentSession.messages[lastAssistantIdx];
            msg.tool_calls = toolCalls.map(tc => ({
                id: tc.id,
                name: tc.name,
                arguments: tc.arguments,
                timestamp: new Date().toISOString(),
                success: false, // Will be updated when result comes
            }));
            this.currentSession.metadata.tool_calls += toolCalls.length;
        }
        if (this.autoSave) {
            this.save();
        }
    }
    /**
     * Log tool result
     */
    logToolResult(toolCallId, result) {
        if (!this.currentSession)
            return;
        // Find the tool call and update it
        for (const msg of this.currentSession.messages) {
            if (msg.tool_calls) {
                const toolCall = msg.tool_calls.find(tc => tc.id === toolCallId);
                if (toolCall) {
                    toolCall.success = result.success;
                    toolCall.output = result.output;
                    toolCall.error = result.error;
                    toolCall.duration_ms = result.duration_ms;
                    break;
                }
            }
        }
        if (this.autoSave) {
            this.save();
        }
    }
    /**
     * Update session cost
     */
    updateCost(cost) {
        if (!this.currentSession)
            return;
        this.currentSession.metadata.estimated_cost = cost;
    }
    /**
     * Save current session to disk
     */
    save() {
        if (!this.currentSession || !this.logPath)
            return;
        const dir = dirname(this.logPath);
        if (!existsSync(dir)) {
            mkdirSync(dir, { recursive: true });
        }
        writeFileSync(this.logPath, JSON.stringify(this.currentSession, null, 2));
    }
    /**
     * Get current session data
     */
    getCurrentSession() {
        return this.currentSession;
    }
    /**
     * Get current session ID
     */
    getCurrentSessionId() {
        return this.currentSession?.metadata.id || null;
    }
    /**
     * Load a session by ID (full or short)
     */
    static loadSession(sessionId) {
        // Try to find the session file
        const files = InteractionLogger.findSessionFiles(sessionId);
        if (files.length === 0)
            return null;
        // If multiple matches, return the most recent
        const path = files[0];
        try {
            const content = readFileSync(path, 'utf-8');
            return JSON.parse(content);
        }
        catch {
            return null;
        }
    }
    /**
     * Search sessions by partial ID
     */
    static searchSessions(partialId) {
        const files = InteractionLogger.findSessionFiles(partialId);
        const sessions = [];
        for (const file of files) {
            try {
                const content = readFileSync(file, 'utf-8');
                sessions.push(JSON.parse(content));
            }
            catch {
                // Skip invalid files
            }
        }
        return sessions;
    }
    /**
     * Find session files matching an ID
     */
    static findSessionFiles(idPattern) {
        if (!existsSync(LOG_DIR))
            return [];
        const results = [];
        const dateDirs = readdirSync(LOG_DIR).filter(d => statSync(join(LOG_DIR, d)).isDirectory());
        for (const dateDir of dateDirs) {
            const dir = join(LOG_DIR, dateDir);
            const files = readdirSync(dir).filter(f => f.endsWith('.json'));
            for (const file of files) {
                const sessionId = file.replace('.json', '');
                // Match full ID or short ID (first 8 chars)
                if (sessionId.startsWith(idPattern) || sessionId.substring(0, 8) === idPattern) {
                    results.push(join(dir, file));
                }
            }
        }
        // Sort by modification time (most recent first)
        return results.sort((a, b) => {
            const statA = statSync(a);
            const statB = statSync(b);
            return statB.mtime.getTime() - statA.mtime.getTime();
        });
    }
    /**
     * Get the latest session
     */
    static getLatestSession() {
        if (!existsSync(LOG_DIR))
            return null;
        // Get all date directories
        const dateDirs = readdirSync(LOG_DIR)
            .filter(d => statSync(join(LOG_DIR, d)).isDirectory())
            .sort()
            .reverse();
        for (const dateDir of dateDirs) {
            const dir = join(LOG_DIR, dateDir);
            const files = readdirSync(dir)
                .filter(f => f.endsWith('.json'))
                .map(f => ({
                path: join(dir, f),
                mtime: statSync(join(dir, f)).mtime.getTime(),
            }))
                .sort((a, b) => b.mtime - a.mtime);
            if (files.length > 0) {
                try {
                    const content = readFileSync(files[0].path, 'utf-8');
                    return JSON.parse(content);
                }
                catch {
                    continue;
                }
            }
        }
        return null;
    }
    /**
     * List all sessions (with pagination)
     */
    static listSessions(options) {
        if (!existsSync(LOG_DIR))
            return { sessions: [], total: 0 };
        const allSessions = [];
        // Get all date directories
        const dateDirs = readdirSync(LOG_DIR)
            .filter(d => statSync(join(LOG_DIR, d)).isDirectory())
            .sort()
            .reverse();
        for (const dateDir of dateDirs) {
            const dir = join(LOG_DIR, dateDir);
            const files = readdirSync(dir).filter(f => f.endsWith('.json'));
            for (const file of files) {
                try {
                    const content = readFileSync(join(dir, file), 'utf-8');
                    const session = JSON.parse(content);
                    // Apply filters
                    if (options?.tags?.length) {
                        const hasTag = options.tags.some(t => session.metadata.tags?.includes(t));
                        if (!hasTag)
                            continue;
                    }
                    if (options?.model && session.metadata.model !== options.model)
                        continue;
                    if (options?.startDate) {
                        const sessionDate = new Date(session.metadata.started_at);
                        if (sessionDate < options.startDate)
                            continue;
                    }
                    if (options?.endDate) {
                        const sessionDate = new Date(session.metadata.started_at);
                        if (sessionDate > options.endDate)
                            continue;
                    }
                    allSessions.push(session.metadata);
                }
                catch {
                    // Skip invalid files
                }
            }
        }
        // Sort by start time (most recent first)
        allSessions.sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime());
        // Apply pagination
        const offset = options?.offset || 0;
        const limit = options?.limit || 20;
        const paginated = allSessions.slice(offset, offset + limit);
        return { sessions: paginated, total: allSessions.length };
    }
    /**
     * Delete a session
     */
    static deleteSession(sessionId) {
        const files = InteractionLogger.findSessionFiles(sessionId);
        if (files.length === 0)
            return false;
        for (const file of files) {
            try {
                unlinkSync(file);
            }
            catch {
                return false;
            }
        }
        return true;
    }
    /**
     * Format session for display
     */
    static formatSession(session) {
        const lines = [];
        const meta = session.metadata;
        lines.push(`Session: ${meta.short_id} (${meta.id})`);
        lines.push(`Started: ${meta.started_at}`);
        if (meta.ended_at)
            lines.push(`Ended: ${meta.ended_at}`);
        lines.push(`Model: ${meta.model} (${meta.provider})`);
        lines.push(`Directory: ${meta.cwd}`);
        lines.push(`Turns: ${meta.turns} | Tool calls: ${meta.tool_calls}`);
        lines.push(`Tokens: ${meta.total_input_tokens} in / ${meta.total_output_tokens} out`);
        lines.push(`Cost: $${meta.estimated_cost.toFixed(4)}`);
        if (meta.tags?.length)
            lines.push(`Tags: ${meta.tags.join(', ')}`);
        if (meta.description)
            lines.push(`Description: ${meta.description}`);
        lines.push('');
        lines.push('Messages:');
        lines.push('-'.repeat(40));
        for (const msg of session.messages) {
            const prefix = `[${msg.timestamp.split('T')[1].split('.')[0]}]`;
            const role = msg.role.toUpperCase().padEnd(10);
            const content = msg.content || '(no content)';
            lines.push(`${prefix} ${role}: ${content.substring(0, 100)}${content.length > 100 ? '...' : ''}`);
            if (msg.tool_calls?.length) {
                for (const tc of msg.tool_calls) {
                    const status = tc.success ? '✓' : '✗';
                    lines.push(`           ${status} Tool: ${tc.name}(${JSON.stringify(tc.arguments).substring(0, 50)}...)`);
                }
            }
        }
        return lines.join('\n');
    }
    /**
     * Dispose resources
     */
    dispose() {
        if (this.saveInterval) {
            clearInterval(this.saveInterval);
            this.saveInterval = null;
        }
        if (this.currentSession) {
            this.endSession();
        }
    }
}
// ============================================================================
// Singleton Export
// ============================================================================
let loggerInstance = null;
export function getInteractionLogger() {
    if (!loggerInstance) {
        loggerInstance = new InteractionLogger({ autoSave: true });
    }
    return loggerInstance;
}
/**
 * Create a new logger instance (for testing)
 */
export function createInteractionLogger(options) {
    return new InteractionLogger(options);
}
//# sourceMappingURL=interaction-logger.js.map