/**
 * LogsCommand - CLI command for viewing and managing logs
 * Commands: zin logs, zin logs --follow, zin logs --level error, zin logs --clear
 */
import { BaseCommand } from '../BaseCommand.js';
import { Logger as FileLogger } from '../logger/Logger.js';
import { Logger } from '../../config/logger.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import fs from '../../node-singletons/fs.js';
import * as path from '../../node-singletons/path.js';
import chalk from 'chalk';
const normalizeLogsOptions = (options) => {
    const level = typeof options['level'] === 'string' ? options['level'] : 'info';
    const clear = options['clear'] === true;
    const follow = options['follow'] === true;
    const linesRaw = typeof options['lines'] === 'string' ? options['lines'] : '50';
    const linesParsed = Number.parseInt(linesRaw, 10);
    const lines = Number.isFinite(linesParsed) ? linesParsed : 50;
    const category = typeof options['category'] === 'string' ? options['category'] : 'app';
    return { level, clear, follow, lines, category };
};
const getLevelColor = (level) => {
    switch (level.toLowerCase()) {
        case 'debug':
            return chalk.gray;
        case 'info':
            return chalk.blue;
        case 'warn':
            return chalk.yellow;
        case 'error':
            return chalk.red;
        default:
            return chalk.white;
    }
};
const printLogEntry = (log) => {
    const timestamp = chalk.gray(log.timestamp);
    const levelColor = getLevelColor(log.level);
    const level = levelColor(`[${log.level.toUpperCase()}]`);
    let output = `${timestamp} ${level} ${log.message}`;
    if (log.data !== undefined && Object.keys(log.data).length > 0) {
        output += ` ${chalk.cyan(JSON.stringify(log.data))}`;
    }
    Logger.info(output);
};
const parseLogEntry = (loggerInstance, line) => {
    const maybe = loggerInstance;
    if (typeof maybe.parseLogEntry !== 'function') {
        throw ErrorFactory.createGeneralError('LoggerInstance does not support parseLogEntry');
    }
    return maybe.parseLogEntry(line);
};
const processLogChunk = (chunk, loggerInstance) => {
    const chunkStr = typeof chunk === 'string' ? chunk : chunk.toString('utf-8');
    const lines = chunkStr.split('\n').filter((line) => line.trim() !== '');
    for (const line of lines) {
        try {
            const entry = parseLogEntry(loggerInstance, line);
            printLogEntry(entry);
        }
        catch (error) {
            ErrorFactory.createTryCatchError('Failed to process log line', error);
        }
    }
};
const displayLogs = (loggerInstance, level, lines, category) => {
    const logs = loggerInstance.getLogs(category, lines);
    if (logs.length === 0) {
        Logger.info(chalk.yellow('ℹ  No logs found'));
        return;
    }
    let filtered = logs;
    if (level !== '' && level !== 'all') {
        filtered = loggerInstance.filterByLevel(logs, level);
    }
    if (filtered.length === 0) {
        Logger.info(chalk.yellow(`ℹ  No logs found with level: ${level}`));
        return;
    }
    Logger.info(chalk.blue(`📋 Last ${filtered.length} log entries (${category}):\n`));
    for (const log of [...filtered].reverse()) {
        printLogEntry(log);
    }
};
const followLogs = (category) => {
    const loggerInstance = FileLogger.getInstance();
    const logsDir = loggerInstance.getLogsDirectory();
    const categoryDir = path.join(logsDir, category);
    if (!fs.existsSync(categoryDir)) {
        Logger.info(chalk.red(`✗ Log category directory not found: ${categoryDir}`));
        return;
    }
    const today = new Date().toISOString().split('T')[0];
    const logFile = path.join(categoryDir, `${today}.log`);
    Logger.info(chalk.blue(`👀 Following logs: ${logFile}\n`));
    Logger.info(chalk.gray('Press Ctrl+C to stop...\n'));
    let lastSize = 0;
    let currentStream = null;
    const safeDestroy = (stream) => {
        try {
            const anyStream = stream;
            if (typeof anyStream.destroy === 'function')
                anyStream.destroy();
        }
        catch {
            // best-effort
        }
    };
    const interval = setInterval(() => {
        if (!fs.existsSync(logFile))
            return;
        const stats = fs.statSync(logFile);
        if (stats.size <= lastSize)
            return;
        // Close previous stream if it exists
        if (currentStream !== null) {
            safeDestroy(currentStream);
        }
        currentStream = fs.createReadStream(logFile, {
            start: lastSize,
            encoding: 'utf-8',
        });
        currentStream.on('data', (chunk) => {
            processLogChunk(chunk, loggerInstance);
        });
        currentStream.on('end', () => {
            // Clean up stream reference when done
            if (currentStream !== null) {
                safeDestroy(currentStream);
                currentStream = null;
            }
        });
        currentStream.on('error', (error) => {
            Logger.error('Stream error', { error });
            if (currentStream !== null) {
                safeDestroy(currentStream);
                currentStream = null;
            }
        });
        lastSize = stats.size;
    }, 1000);
    process.on('SIGINT', () => {
        clearInterval(interval);
        // Clean up stream on exit
        if (currentStream !== null) {
            safeDestroy(currentStream);
            currentStream = null;
        }
        Logger.info(chalk.yellow('\n\nLog following stopped'));
        process.exit(0);
    });
};
const clearLogs = (loggerInstance, category) => {
    const success = loggerInstance.clearLogs(category);
    if (success === true) {
        Logger.info(chalk.green(`✓ Cleared logs for category: ${category}`));
    }
    else {
        Logger.info(chalk.red(`✗ Failed to clear logs for category: ${category}`));
    }
};
const executeLogs = (options) => {
    const normalized = normalizeLogsOptions(options);
    const loggerInstance = FileLogger.getInstance();
    if (options['lines'] !== undefined && Number.isNaN(Number(options['lines']))) {
        throw ErrorFactory.createGeneralError('Lines must be a number');
    }
    if (normalized.clear) {
        clearLogs(loggerInstance, normalized.category);
        return;
    }
    if (normalized.follow) {
        followLogs(normalized.category);
        return;
    }
    displayLogs(loggerInstance, normalized.level, normalized.lines, normalized.category);
};
export const LogsCommand = Object.freeze({
    /**
     * Create a new logs command instance
     */
    create() {
        const addOptions = (command) => {
            command
                .option('-l, --level <level>', 'Filter by log level (debug, info, warn, error)', 'info')
                .option('-c, --clear', 'Clear all logs')
                .option('-f, --follow', 'Follow logs in real-time (like tail -f)')
                .option('-n, --lines <number>', 'Number of lines to show', '50')
                .option('--category <category>', 'Log category (app, cli, errors, migrations, debug)', 'app');
        };
        const cmd = BaseCommand.create({
            name: 'logs',
            description: 'View and manage application logs',
            addOptions,
            execute: (options) => executeLogs(options),
        });
        return cmd;
    },
    /**
     * Register the command with Commander
     * @deprecated Use create() instead
     */
    register(program) {
        const cmd = program.command('logs');
        cmd.description('View and manage application logs');
        cmd
            .option('-l, --level <level>', 'Filter by log level (debug, info, warn, error)', 'info')
            .option('-c, --clear', 'Clear all logs')
            .option('-f, --follow', 'Follow logs in real-time (like tail -f)')
            .option('-n, --lines <number>', 'Number of lines to show', '50')
            .option('--category <category>', 'Log category (app, cli, errors, migrations, debug)', 'app');
        cmd.action((options) => {
            executeLogs(options);
        });
    },
});
export default LogsCommand;
