/**
 * Structured Logger for Code Buddy
 * Provides consistent logging across the application with levels and context
 *
 * Environment Variables:
 * - DEBUG=true or DEBUG=1: Enable debug mode (sets log level to 'debug')
 * - LOG_LEVEL=debug|info|warn|error: Set specific log level
 * - LOG_FORMAT=json|text: Output format (default: text)
 * - LOG_FILE=path: Write logs to file (in addition to console)
 * - NO_COLOR=1: Disable colored output
 */
import chalk from 'chalk';
import * as fs from 'fs';
import * as path from 'path';
const LOG_LEVEL_PRIORITY = {
    debug: 0,
    info: 1,
    warn: 2,
    error: 3,
};
const LOG_LEVEL_COLORS = {
    debug: chalk.gray,
    info: chalk.blue,
    warn: chalk.yellow,
    error: chalk.red,
};
const LOG_LEVEL_ICONS = {
    debug: '🔍',
    info: 'ℹ️',
    warn: '⚠️',
    error: '❌',
};
/**
 * Check if debug mode is enabled via environment
 */
function isDebugEnabled() {
    const debug = process.env.DEBUG;
    return debug === 'true' || debug === '1' || debug === 'codebuddy';
}
/**
 * Get log level from environment
 */
function getLogLevelFromEnv() {
    if (isDebugEnabled()) {
        return 'debug';
    }
    const level = process.env.LOG_LEVEL?.toLowerCase();
    if (level && ['debug', 'info', 'warn', 'error'].includes(level)) {
        return level;
    }
    return 'info';
}
/**
 * Structured Logger class
 */
export class Logger {
    options;
    logHistory = [];
    maxHistorySize = 1000;
    fileStream;
    constructor(options = {}) {
        this.options = {
            level: getLogLevelFromEnv(),
            format: process.env.LOG_FORMAT || 'text',
            enableColors: !process.env.NO_COLOR && (process.stdout.isTTY ?? true),
            enableTimestamps: true,
            silent: process.env.NODE_ENV === 'test',
            logFile: process.env.LOG_FILE,
            ...options,
        };
        // Initialize file stream if configured
        if (this.options.logFile) {
            this.initializeFileLogging(this.options.logFile);
        }
    }
    /**
     * Initialize file logging
     */
    initializeFileLogging(logFile) {
        try {
            const logDir = path.dirname(logFile);
            if (!fs.existsSync(logDir)) {
                fs.mkdirSync(logDir, { recursive: true });
            }
            this.fileStream = fs.createWriteStream(logFile, { flags: 'a' });
        }
        catch (err) {
            console.error(`Failed to initialize log file: ${logFile}`, err);
        }
    }
    /**
     * Create a child logger with additional context
     */
    child(source) {
        return new Logger({
            ...this.options,
            source,
        });
    }
    /**
     * Set log level
     */
    setLevel(level) {
        this.options.level = level;
    }
    /**
     * Get current log level
     */
    getLevel() {
        return this.options.level;
    }
    /**
     * Enable or disable silent mode
     */
    setSilent(silent) {
        this.options.silent = silent;
    }
    /**
     * Check if a log level should be output
     */
    shouldLog(level) {
        return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.options.level];
    }
    /**
     * Format timestamp
     */
    formatTimestamp() {
        return new Date().toISOString();
    }
    /**
     * Format log entry for output
     */
    /** Safe JSON.stringify that handles circular references and Error objects */
    safeStringify(obj) {
        const seen = new WeakSet();
        return JSON.stringify(obj, (_key, value) => {
            if (value instanceof Error) {
                return { name: value.name, message: value.message, stack: value.stack };
            }
            if (typeof value === 'object' && value !== null) {
                if (seen.has(value))
                    return '[Circular]';
                seen.add(value);
            }
            return value;
        });
    }
    formatEntry(entry) {
        // JSON format
        if (this.options.format === 'json') {
            return this.safeStringify({
                timestamp: entry.timestamp,
                level: entry.level,
                source: entry.source,
                message: entry.message,
                ...entry.context,
            });
        }
        // Text format
        const parts = [];
        const color = this.options.enableColors ? LOG_LEVEL_COLORS[entry.level] : (s) => s;
        const icon = this.options.enableColors ? LOG_LEVEL_ICONS[entry.level] : '';
        // Timestamp
        if (this.options.enableTimestamps) {
            parts.push(chalk.gray(`[${entry.timestamp}]`));
        }
        // Level with icon
        parts.push(color(`${icon} ${entry.level.toUpperCase().padEnd(5)}`));
        // Source
        if (entry.source) {
            parts.push(chalk.cyan(`[${entry.source}]`));
        }
        // Message
        parts.push(entry.message);
        // Context
        if (entry.context && Object.keys(entry.context).length > 0) {
            const contextStr = this.safeStringify(entry.context);
            parts.push(chalk.gray(contextStr));
        }
        return parts.join(' ');
    }
    /**
     * Core log method
     */
    log(level, message, context) {
        if (!this.shouldLog(level)) {
            return;
        }
        const entry = {
            timestamp: this.formatTimestamp(),
            level,
            message,
            context,
            source: this.options.source,
        };
        // Store in history
        this.logHistory.push(entry);
        if (this.logHistory.length > this.maxHistorySize) {
            this.logHistory.shift();
        }
        // Output to console
        if (!this.options.silent) {
            const formatted = this.formatEntry(entry);
            // Route all log levels to stderr to avoid polluting stdout
            // (important for headless --output json mode)
            console.error(formatted);
        }
        // Output to file (always JSON for easy parsing)
        if (this.fileStream) {
            const jsonEntry = this.safeStringify({
                timestamp: entry.timestamp,
                level: entry.level,
                source: entry.source,
                message: entry.message,
                ...entry.context,
            });
            this.fileStream.write(jsonEntry + '\n');
        }
    }
    /**
     * Check if debug logging is enabled
     */
    isDebugEnabled() {
        return this.options.level === 'debug';
    }
    /**
     * Close file stream
     */
    close() {
        if (this.fileStream) {
            this.fileStream.end();
            this.fileStream = undefined;
        }
    }
    /**
     * Debug level log
     */
    debug(message, context) {
        this.log('debug', message, context);
    }
    /**
     * Info level log
     */
    info(message, context) {
        this.log('info', message, context);
    }
    /**
     * Warning level log
     */
    warn(message, context) {
        this.log('warn', message, context);
    }
    error(message, errorOrContext, context) {
        let finalContext = {};
        if (errorOrContext instanceof Error) {
            finalContext = {
                errorName: errorOrContext.name,
                errorMessage: errorOrContext.message,
                errorStack: errorOrContext.stack,
                ...context,
            };
        }
        else if (errorOrContext) {
            finalContext = errorOrContext;
        }
        this.log('error', message, finalContext);
    }
    /**
     * Log with timing
     */
    time(label) {
        const start = Date.now();
        this.debug(`Timer started: ${label}`);
        return () => {
            const duration = Date.now() - start;
            this.debug(`Timer ended: ${label}`, { durationMs: duration });
        };
    }
    /**
     * Get log history
     */
    getHistory() {
        return [...this.logHistory];
    }
    /**
     * Clear log history
     */
    clearHistory() {
        this.logHistory = [];
    }
    /**
     * Export logs as JSON
     */
    exportLogsAsJSON() {
        return JSON.stringify(this.logHistory, null, 2);
    }
}
// Default logger instance
let defaultLogger = null;
/**
 * Get the default logger instance
 */
export function getLogger() {
    if (!defaultLogger) {
        defaultLogger = new Logger();
    }
    return defaultLogger;
}
/**
 * Create a new logger instance
 */
export function createLogger(options) {
    return new Logger(options);
}
/**
 * Reset the default logger (for testing)
 */
export function resetLogger() {
    defaultLogger = null;
}
// Convenience exports for direct use
export const logger = {
    debug: (message, context) => getLogger().debug(message, context),
    info: (message, context) => getLogger().info(message, context),
    warn: (message, context) => getLogger().warn(message, context),
    error: (message, errorOrContext, context) => {
        if (errorOrContext instanceof Error) {
            getLogger().error(message, errorOrContext, context);
        }
        else {
            getLogger().error(message, errorOrContext);
        }
    },
    isDebugEnabled: () => getLogger().isDebugEnabled(),
    time: (label) => getLogger().time(label),
    child: (source) => getLogger().child(source),
};
/**
 * Check if debug mode is enabled (for use outside logger)
 */
export { isDebugEnabled };
/**
 * Debug utility - only logs if DEBUG is enabled
 * Usage: debug('message') or debug('message', { context })
 */
export function debug(message, context) {
    if (isDebugEnabled()) {
        getLogger().debug(message, context);
    }
}
//# sourceMappingURL=logger.js.map