/**
 * Enhanced Debug Logger for Grok CLI
 *
 * Provides comprehensive debugging capabilities:
 * - Verbose API call logging with full request/response dumps
 * - Detailed timing for each operation step
 * - Tool call tracing with full context
 * - Structured JSON output for analysis
 *
 * Activation:
 * - --debug flag on CLI
 * - DEBUG=true or DEBUG=1 environment variable
 * - DEBUG=codebuddy for component-specific debug
 *
 * Environment Variables:
 * - DEBUG=true|1|codebuddy: Enable debug mode
 * - DEBUG_LEVEL=verbose|normal|minimal: Debug verbosity
 * - DEBUG_OUTPUT=console|file|both: Output destination
 * - DEBUG_FILE=path: Custom debug log file path
 * - DEBUG_JSON=true: Force JSON format for all output
 * - DEBUG_TIMING=true: Enable detailed timing measurements
 * - DEBUG_API=true: Log full API requests/responses
 * - DEBUG_TOOLS=true: Log tool call details
 * - DEBUG_PROMPTS=true: Dump system/user prompts
 */
import chalk from 'chalk';
import * as fs from 'fs';
import * as path from 'path';
/**
 * Check if debug mode is enabled via environment or flag
 */
function isDebugModeEnabled() {
    const debug = process.env.DEBUG;
    return (debug === 'true' ||
        debug === '1' ||
        debug === 'codebuddy' ||
        process.env.GROK_DEBUG === 'true');
}
/**
 * Get debug level from environment
 */
function getDebugLevel() {
    const level = process.env.DEBUG_LEVEL?.toLowerCase();
    if (level === 'verbose' || level === 'normal' || level === 'minimal') {
        return level;
    }
    return 'normal';
}
/**
 * Get debug output mode from environment
 */
function getDebugOutput() {
    const output = process.env.DEBUG_OUTPUT?.toLowerCase();
    if (output === 'console' || output === 'file' || output === 'both') {
        return output;
    }
    return 'console';
}
/**
 * Enhanced Debug Logger class
 */
export class DebugLogger {
    config;
    fileStream;
    timings = new Map();
    apiLogs = [];
    toolLogs = [];
    promptDumps = [];
    requestCounter = 0;
    sessionStartTime;
    constructor(config) {
        this.sessionStartTime = Date.now();
        this.config = {
            enabled: config?.enabled ?? isDebugModeEnabled(),
            level: config?.level ?? getDebugLevel(),
            output: config?.output ?? getDebugOutput(),
            filePath: config?.filePath ?? process.env.DEBUG_FILE,
            jsonFormat: config?.jsonFormat ?? (process.env.DEBUG_JSON === 'true'),
            timing: config?.timing ?? (process.env.DEBUG_TIMING === 'true' || isDebugModeEnabled()),
            api: config?.api ?? (process.env.DEBUG_API === 'true' || isDebugModeEnabled()),
            tools: config?.tools ?? (process.env.DEBUG_TOOLS === 'true' || isDebugModeEnabled()),
            prompts: config?.prompts ?? (process.env.DEBUG_PROMPTS === 'true'),
        };
        if (this.config.enabled && (this.config.output === 'file' || this.config.output === 'both')) {
            this.initializeFileLogging();
        }
        if (this.config.enabled) {
            this.logDebugBanner();
        }
    }
    /**
     * Initialize file logging for debug output
     */
    initializeFileLogging() {
        const defaultPath = path.join(process.cwd(), '.codebuddy', 'debug.log');
        const logPath = this.config.filePath || defaultPath;
        try {
            const logDir = path.dirname(logPath);
            if (!fs.existsSync(logDir)) {
                fs.mkdirSync(logDir, { recursive: true });
            }
            this.fileStream = fs.createWriteStream(logPath, { flags: 'a' });
            this.writeToFile(`\n${'='.repeat(80)}\nDebug session started: ${new Date().toISOString()}\n${'='.repeat(80)}\n`);
        }
        catch (err) {
            console.error(`Failed to initialize debug log file: ${logPath}`, err);
        }
    }
    /**
     * Log debug mode banner
     */
    logDebugBanner() {
        const banner = `
${chalk.yellow('=')}${chalk.yellow('='.repeat(60))}${chalk.yellow('=')}
${chalk.yellow('DEBUG MODE ENABLED')}
${chalk.gray('Level:')} ${this.config.level}
${chalk.gray('Output:')} ${this.config.output}
${chalk.gray('API Logging:')} ${this.config.api ? 'ON' : 'OFF'}
${chalk.gray('Tool Tracing:')} ${this.config.tools ? 'ON' : 'OFF'}
${chalk.gray('Timing:')} ${this.config.timing ? 'ON' : 'OFF'}
${chalk.gray('Prompt Dump:')} ${this.config.prompts ? 'ON' : 'OFF'}
${chalk.yellow('=')}${chalk.yellow('='.repeat(60))}${chalk.yellow('=')}
`;
        if (this.config.output !== 'file') {
            console.log(banner);
        }
    }
    /**
     * Check if debug is enabled
     */
    isEnabled() {
        return this.config.enabled;
    }
    /**
     * Enable or disable debug mode
     */
    setEnabled(enabled) {
        this.config.enabled = enabled;
        if (enabled && !this.fileStream && (this.config.output === 'file' || this.config.output === 'both')) {
            this.initializeFileLogging();
        }
    }
    /**
     * Update debug configuration
     */
    updateConfig(config) {
        Object.assign(this.config, config);
    }
    /**
     * Write to file stream
     */
    writeToFile(content) {
        if (this.fileStream) {
            this.fileStream.write(content + '\n');
        }
    }
    /**
     * Format and output debug message
     */
    output(category, message, data) {
        if (!this.config.enabled)
            return;
        const timestamp = new Date().toISOString();
        const elapsed = Date.now() - this.sessionStartTime;
        if (this.config.jsonFormat) {
            const jsonOutput = JSON.stringify({
                timestamp,
                elapsedMs: elapsed,
                category,
                message,
                ...data,
            });
            if (this.config.output !== 'file') {
                console.log(jsonOutput);
            }
            this.writeToFile(jsonOutput);
        }
        else {
            const prefix = chalk.gray(`[${timestamp}]`) + chalk.cyan(` [${category}]`);
            const formattedMessage = `${prefix} ${message}`;
            if (this.config.output !== 'file') {
                console.log(formattedMessage);
                if (data && this.config.level === 'verbose') {
                    console.log(chalk.gray(JSON.stringify(data, null, 2)));
                }
            }
            this.writeToFile(`[${timestamp}] [${category}] ${message}`);
            if (data) {
                this.writeToFile(JSON.stringify(data, null, 2));
            }
        }
    }
    // ============================================================================
    // Timing Methods
    // ============================================================================
    /**
     * Start a timing measurement
     */
    startTiming(label, metadata) {
        if (!this.config.enabled || !this.config.timing)
            return;
        const entry = {
            label,
            startTime: performance.now(),
            metadata,
        };
        this.timings.set(label, entry);
        this.output('TIMING', `Started: ${label}`, metadata);
    }
    /**
     * End a timing measurement and return duration
     */
    endTiming(label, additionalMetadata) {
        if (!this.config.enabled || !this.config.timing)
            return undefined;
        const entry = this.timings.get(label);
        if (!entry) {
            this.output('TIMING', `Warning: No timing entry found for "${label}"`);
            return undefined;
        }
        entry.endTime = performance.now();
        entry.durationMs = entry.endTime - entry.startTime;
        if (additionalMetadata) {
            entry.metadata = { ...entry.metadata, ...additionalMetadata };
        }
        const durationStr = entry.durationMs.toFixed(2);
        this.output('TIMING', `Completed: ${label} (${durationStr}ms)`, {
            durationMs: entry.durationMs,
            ...entry.metadata,
        });
        return entry.durationMs;
    }
    /**
     * Create a timing wrapper for async functions
     */
    async timeAsync(label, fn, metadata) {
        this.startTiming(label, metadata);
        try {
            const result = await fn();
            this.endTiming(label, { success: true });
            return result;
        }
        catch (error) {
            this.endTiming(label, { success: false, error: String(error) });
            throw error;
        }
    }
    /**
     * Get all timing entries
     */
    getTimings() {
        return Array.from(this.timings.values());
    }
    // ============================================================================
    // API Call Logging
    // ============================================================================
    /**
     * Log an API request
     */
    logAPIRequest(method, endpoint, model, messages, tools, options) {
        if (!this.config.enabled || !this.config.api)
            return '';
        const requestId = `req_${++this.requestCounter}_${Date.now()}`;
        const log = {
            timestamp: new Date().toISOString(),
            requestId,
            method,
            endpoint,
            model,
            messages: messages.map((m) => ({
                role: m.role,
                contentPreview: this.truncate(String(m.content || ''), 200),
                contentLength: String(m.content || '').length,
                hasToolCalls: Array.isArray(m.tool_calls) && m.tool_calls.length > 0,
            })),
            tools: tools?.map((t) => ({
                name: t.function.name,
                description: this.truncate(t.function.description, 100),
            })),
            options,
            durationMs: 0,
        };
        this.apiLogs.push(log);
        this.output('API-REQUEST', `${method} ${endpoint}`, {
            requestId,
            model,
            messageCount: messages.length,
            toolCount: tools?.length || 0,
        });
        // Dump full prompts if enabled
        if (this.config.prompts) {
            for (const msg of messages) {
                this.dumpPrompt(msg.role, String(msg.content || ''));
            }
        }
        return requestId;
    }
    /**
     * Log an API response
     */
    logAPIResponse(requestId, status, content, toolCalls, usage, durationMs) {
        if (!this.config.enabled || !this.config.api)
            return;
        const log = this.apiLogs.find((l) => l.requestId === requestId);
        if (log) {
            log.response = {
                status,
                contentPreview: this.truncate(content || '', 200),
                contentLength: (content || '').length,
                toolCalls: toolCalls?.map((tc) => ({
                    id: tc.id,
                    name: tc.function.name,
                    argumentsPreview: this.truncate(tc.function.arguments, 100),
                })),
                usage: usage
                    ? {
                        promptTokens: usage.prompt_tokens,
                        completionTokens: usage.completion_tokens,
                        totalTokens: usage.total_tokens,
                    }
                    : undefined,
            };
            log.durationMs = durationMs || 0;
        }
        this.output('API-RESPONSE', `${requestId} - ${status}`, {
            requestId,
            contentLength: (content || '').length,
            toolCallCount: toolCalls?.length || 0,
            usage,
            durationMs,
        });
    }
    /**
     * Log an API error
     */
    logAPIError(requestId, error, durationMs) {
        if (!this.config.enabled || !this.config.api)
            return;
        const log = this.apiLogs.find((l) => l.requestId === requestId);
        if (log) {
            log.error = {
                name: error.name,
                message: error.message,
                stack: error.stack,
            };
            log.durationMs = durationMs || 0;
        }
        this.output('API-ERROR', `${requestId} - ${error.name}: ${error.message}`, {
            requestId,
            errorName: error.name,
            errorMessage: error.message,
            durationMs,
        });
    }
    // ============================================================================
    // Tool Call Tracing
    // ============================================================================
    /**
     * Log a tool call
     */
    logToolCall(callId, toolName, argumentsRaw, roundNumber) {
        if (!this.config.enabled || !this.config.tools)
            return;
        let parsedArgs = {};
        try {
            parsedArgs = JSON.parse(argumentsRaw);
        }
        catch {
            parsedArgs = { raw: argumentsRaw };
        }
        const log = {
            timestamp: new Date().toISOString(),
            callId,
            toolName,
            arguments: parsedArgs,
            argumentsRaw,
            durationMs: 0,
            roundNumber,
        };
        this.toolLogs.push(log);
        this.output('TOOL-CALL', `[Round ${roundNumber}] ${toolName}`, {
            callId,
            toolName,
            arguments: this.config.level === 'verbose' ? parsedArgs : this.summarizeArgs(parsedArgs),
            roundNumber,
        });
    }
    /**
     * Log a tool result
     */
    logToolResult(callId, success, output, error, durationMs) {
        if (!this.config.enabled || !this.config.tools)
            return;
        const log = this.toolLogs.find((l) => l.callId === callId);
        if (log) {
            log.result = {
                success,
                output: this.config.level === 'verbose' ? output : this.truncate(output || '', 200),
                outputLength: output?.length,
                error,
            };
            log.durationMs = durationMs || 0;
        }
        const statusIcon = success ? chalk.green('[OK]') : chalk.red('[FAIL]');
        this.output('TOOL-RESULT', `${statusIcon} ${callId}`, {
            callId,
            success,
            outputLength: output?.length,
            error,
            durationMs,
        });
    }
    // ============================================================================
    // Prompt Dumping
    // ============================================================================
    /**
     * Dump a prompt for analysis
     */
    dumpPrompt(type, content, metadata) {
        if (!this.config.enabled || !this.config.prompts)
            return;
        const dump = {
            timestamp: new Date().toISOString(),
            type,
            content,
            contentLength: content.length,
            tokenEstimate: Math.ceil(content.length / 4), // Rough estimate
            metadata,
        };
        this.promptDumps.push(dump);
        if (this.config.level === 'verbose') {
            this.output('PROMPT', `[${type.toUpperCase()}] (${content.length} chars)`, {
                type,
                contentLength: content.length,
                preview: this.truncate(content, 500),
            });
        }
    }
    // ============================================================================
    // General Debug Methods
    // ============================================================================
    /**
     * Log a debug message
     */
    debug(message, context) {
        if (!this.config.enabled)
            return;
        this.output('DEBUG', message, context);
    }
    /**
     * Log verbose debug message (only in verbose mode)
     */
    verbose(message, context) {
        if (!this.config.enabled || this.config.level !== 'verbose')
            return;
        this.output('VERBOSE', message, context);
    }
    /**
     * Log a trace message with stack
     */
    trace(message, context) {
        if (!this.config.enabled)
            return;
        const stack = new Error().stack?.split('\n').slice(2).join('\n');
        this.output('TRACE', message, { ...context, stack });
    }
    // ============================================================================
    // Summary and Export
    // ============================================================================
    /**
     * Get debug session summary
     */
    getSessionSummary() {
        const sessionDuration = Date.now() - this.sessionStartTime;
        const totalApiDuration = this.apiLogs.reduce((sum, l) => sum + l.durationMs, 0);
        const avgApiDuration = this.apiLogs.length > 0 ? totalApiDuration / this.apiLogs.length : 0;
        const toolsWithResults = this.toolLogs.filter((l) => l.result);
        const totalToolDuration = toolsWithResults.reduce((sum, l) => sum + l.durationMs, 0);
        const avgToolDuration = toolsWithResults.length > 0 ? totalToolDuration / toolsWithResults.length : 0;
        const successfulTools = toolsWithResults.filter((l) => l.result?.success).length;
        const toolSuccessRate = toolsWithResults.length > 0 ? (successfulTools / toolsWithResults.length) * 100 : 100;
        return {
            sessionDuration,
            apiCallCount: this.apiLogs.length,
            totalApiDuration,
            avgApiDuration,
            toolCallCount: this.toolLogs.length,
            totalToolDuration,
            avgToolDuration,
            toolSuccessRate,
            timingEntries: this.timings.size,
        };
    }
    /**
     * Format session summary for display
     */
    formatSessionSummary() {
        const summary = this.getSessionSummary();
        return `
${chalk.yellow('=')}${chalk.yellow('='.repeat(40))}${chalk.yellow('=')}
${chalk.yellow('DEBUG SESSION SUMMARY')}
${chalk.gray('Duration:')} ${(summary.sessionDuration / 1000).toFixed(2)}s

${chalk.cyan('API Calls:')}
  Total: ${summary.apiCallCount}
  Total Duration: ${summary.totalApiDuration.toFixed(0)}ms
  Avg Duration: ${summary.avgApiDuration.toFixed(0)}ms

${chalk.cyan('Tool Calls:')}
  Total: ${summary.toolCallCount}
  Total Duration: ${summary.totalToolDuration.toFixed(0)}ms
  Avg Duration: ${summary.avgToolDuration.toFixed(0)}ms
  Success Rate: ${summary.toolSuccessRate.toFixed(1)}%

${chalk.cyan('Timing Entries:')} ${summary.timingEntries}
${chalk.yellow('=')}${chalk.yellow('='.repeat(40))}${chalk.yellow('=')}
`;
    }
    /**
     * Export all debug data as JSON
     */
    exportAsJSON() {
        return JSON.stringify({
            sessionStartTime: new Date(this.sessionStartTime).toISOString(),
            config: this.config,
            summary: this.getSessionSummary(),
            apiLogs: this.apiLogs,
            toolLogs: this.toolLogs,
            promptDumps: this.promptDumpts,
            timings: Array.from(this.timings.values()),
        }, null, 2);
    }
    /**
     * Clear all logs
     */
    clear() {
        this.apiLogs = [];
        this.toolLogs = [];
        this.promptDumps = [];
        this.timings.clear();
        this.contextSnapshots = [];
        this.stackTraces = [];
    }
    // ============================================================================
    // Context Snapshot Management
    // ============================================================================
    contextSnapshots = [];
    stackTraces = [];
    /**
     * Capture a context snapshot
     */
    captureContext(label, context) {
        if (!this.config.enabled)
            return;
        const snapshot = {
            timestamp: new Date().toISOString(),
            label,
            conversationLength: context.conversationHistory?.length || 0,
            contextLength: context.currentContext?.length || 0,
            systemPromptLength: context.systemPrompt?.length || 0,
            activeTools: context.activeTools || [],
            model: context.model,
            tokenCount: context.tokenCount,
            snapshot: {
                conversationPreview: context.conversationHistory?.slice(-3).map(m => ({
                    type: m.type,
                    preview: m.content.substring(0, 100),
                })),
                contextPreview: context.currentContext?.substring(0, 500),
                systemPromptPreview: context.systemPrompt?.substring(0, 300),
            },
        };
        this.contextSnapshots.push(snapshot);
        this.output('CONTEXT', `Snapshot: ${label}`, {
            conversationLength: snapshot.conversationLength,
            contextLength: snapshot.contextLength,
            tokenCount: snapshot.tokenCount,
        });
    }
    /**
     * Get context snapshots
     */
    getContextSnapshots() {
        return [...this.contextSnapshots];
    }
    // ============================================================================
    // Stack Trace Management
    // ============================================================================
    /**
     * Capture a detailed stack trace with context
     */
    captureStackTrace(label, error, metadata) {
        if (!this.config.enabled)
            return;
        const stack = error?.stack || new Error('Stack Trace').stack || '';
        const cleanedStack = this.cleanStackTrace(stack);
        const entry = {
            timestamp: new Date().toISOString(),
            label,
            stack: cleanedStack,
            errorName: error?.name,
            errorMessage: error?.message,
            metadata,
        };
        this.stackTraces.push(entry);
        this.output('STACK', label, {
            errorName: error?.name,
            errorMessage: error?.message,
            stackDepth: cleanedStack.split('\n').length,
        });
    }
    /**
     * Get stack traces
     */
    getStackTraces() {
        return [...this.stackTraces];
    }
    /**
     * Clean and format a stack trace
     */
    cleanStackTrace(stack) {
        return stack
            .split('\n')
            .filter(line => !line.includes('debug-logger.ts'))
            .filter(line => !line.includes('node:internal'))
            .slice(0, 20) // Limit depth
            .join('\n');
    }
    // ============================================================================
    // Request Replay Support
    // ============================================================================
    /**
     * Get a specific API log by request ID
     */
    getAPILog(requestId) {
        return this.apiLogs.find(l => l.requestId === requestId);
    }
    /**
     * Get API logs for replay
     */
    getAPILogs() {
        return [...this.apiLogs];
    }
    /**
     * Get tool logs
     */
    getToolLogs() {
        return [...this.toolLogs];
    }
    /**
     * Get a tool call by ID
     */
    getToolLog(callId) {
        return this.toolLogs.find(l => l.callId === callId);
    }
    /**
     * Close file stream
     */
    close() {
        if (this.config.enabled) {
            this.output('DEBUG', 'Session ended');
            this.output('SUMMARY', this.formatSessionSummary());
        }
        if (this.fileStream) {
            this.fileStream.end();
            this.fileStream = undefined;
        }
    }
    // ============================================================================
    // Utility Methods
    // ============================================================================
    truncate(str, maxLength) {
        if (str.length <= maxLength)
            return str;
        return str.substring(0, maxLength) + '...';
    }
    summarizeArgs(args) {
        const summary = {};
        for (const [key, value] of Object.entries(args)) {
            if (typeof value === 'string' && value.length > 100) {
                summary[key] = `<string:${value.length} chars>`;
            }
            else if (Array.isArray(value)) {
                summary[key] = `<array:${value.length} items>`;
            }
            else if (typeof value === 'object' && value !== null) {
                summary[key] = `<object:${Object.keys(value).length} keys>`;
            }
            else {
                summary[key] = value;
            }
        }
        return summary;
    }
    // Fix typo in exportAsJSON
    get promptDumpts() {
        return this.promptDumps;
    }
}
// ============================================================================
// Singleton Instance
// ============================================================================
let debugLoggerInstance = null;
/**
 * Get the global DebugLogger instance
 */
export function getDebugLogger() {
    if (!debugLoggerInstance) {
        debugLoggerInstance = new DebugLogger();
    }
    return debugLoggerInstance;
}
/**
 * Create a new DebugLogger instance with custom config
 */
export function createDebugLogger(config) {
    return new DebugLogger(config);
}
/**
 * Reset the global DebugLogger instance (for testing)
 */
export function resetDebugLogger() {
    if (debugLoggerInstance) {
        debugLoggerInstance.close();
        debugLoggerInstance = null;
    }
}
/**
 * Quick debug check - returns true if debug mode is enabled
 */
export function isDebugEnabled() {
    return getDebugLogger().isEnabled();
}
// Convenience export for direct use
export const debugLogger = {
    isEnabled: () => getDebugLogger().isEnabled(),
    setEnabled: (enabled) => getDebugLogger().setEnabled(enabled),
    updateConfig: (config) => getDebugLogger().updateConfig(config),
    debug: (message, context) => getDebugLogger().debug(message, context),
    verbose: (message, context) => getDebugLogger().verbose(message, context),
    trace: (message, context) => getDebugLogger().trace(message, context),
    startTiming: (label, metadata) => getDebugLogger().startTiming(label, metadata),
    endTiming: (label, metadata) => getDebugLogger().endTiming(label, metadata),
    timeAsync: (label, fn, metadata) => getDebugLogger().timeAsync(label, fn, metadata),
    getTimings: () => getDebugLogger().getTimings(),
    logAPIRequest: (...args) => getDebugLogger().logAPIRequest(...args),
    logAPIResponse: (...args) => getDebugLogger().logAPIResponse(...args),
    logAPIError: (...args) => getDebugLogger().logAPIError(...args),
    getAPILog: (requestId) => getDebugLogger().getAPILog(requestId),
    getAPILogs: () => getDebugLogger().getAPILogs(),
    logToolCall: (...args) => getDebugLogger().logToolCall(...args),
    logToolResult: (...args) => getDebugLogger().logToolResult(...args),
    getToolLog: (callId) => getDebugLogger().getToolLog(callId),
    getToolLogs: () => getDebugLogger().getToolLogs(),
    dumpPrompt: (...args) => getDebugLogger().dumpPrompt(...args),
    captureContext: (...args) => getDebugLogger().captureContext(...args),
    getContextSnapshots: () => getDebugLogger().getContextSnapshots(),
    captureStackTrace: (...args) => getDebugLogger().captureStackTrace(...args),
    getStackTraces: () => getDebugLogger().getStackTraces(),
    getSessionSummary: () => getDebugLogger().getSessionSummary(),
    formatSessionSummary: () => getDebugLogger().formatSessionSummary(),
    exportAsJSON: () => getDebugLogger().exportAsJSON(),
    clear: () => getDebugLogger().clear(),
    close: () => getDebugLogger().close(),
};
//# sourceMappingURL=debug-logger.js.map