import chalk from "chalk";

export enum LogLevel {
    DEBUG = 0,
    INFO = 1,
    WARN = 2,
    ERROR = 3,
}

let LOG_LEVEL = LogLevel.INFO;

export function setLogLevel(level: LogLevel) {
    LOG_LEVEL = level;
}

function formatTimestamp(): string {
    const now = new Date();
    return now.toISOString();
}

export function log(level: LogLevel, message: string, data?: any): void {
    if (level < LOG_LEVEL) return;

    const timestamp = formatTimestamp();
    const levelStr = LogLevel[level];

    // Get line number from stack trace
    const stackLines = new Error().stack?.split('\n') || [];
    const callerLine = stackLines[3]?.trim() || '';
    const lineMatch = callerLine.match(/:(\d+):\d+\)?$/);
    const lineNumber = lineMatch ? lineMatch[1] : '?';

    let coloredLevel: string;
    let coloredMessage = message;
    switch (level) {
        case LogLevel.DEBUG:
            coloredLevel = chalk.cyan(`[${levelStr}]`);
            break;
        case LogLevel.INFO:
            coloredLevel = chalk.green(`[${levelStr}]`);
            break;
        case LogLevel.WARN:
            coloredLevel = chalk.yellow(`[${levelStr}]`);
            coloredMessage = chalk.yellow(message);
            break;
        case LogLevel.ERROR:
            coloredLevel = chalk.red(`[${levelStr}]`);
            coloredMessage = chalk.red(message);
            break;
        default:
            coloredLevel = `[${levelStr}]`;
    }
    const coloredTimestamp = chalk.gray(`[${timestamp}]`);
    const coloredLineNumber = chalk.magenta(`[L${lineNumber}]`);
    let logMessage = `${coloredTimestamp} ${coloredLevel} ${coloredLineNumber} ${coloredMessage}`;
    if (data !== undefined) {
        if (typeof data === "object") {
            try {
                const dataToLog = (level === LogLevel.DEBUG || !Array.isArray(data) || data.length < 10)
                    ? data
                    : `[Array of length ${data.length}]`;
                const dataStr = JSON.stringify(dataToLog, null, 2);
                logMessage += `\n${chalk.blue(dataStr)}`;
            } catch (e) {
                logMessage += `\n${chalk.red("[Unable to stringify data]")}`;
            }
        } else {
            logMessage += ` ${data}`;
        }
    }
    console.log(logMessage);
}

export function logDebug(message: string, data?: any): void {
    log(LogLevel.DEBUG, message, data);
}
export function logInfo(message: string, data?: any): void {
    log(LogLevel.INFO, message, data);
}
export function logWarn(message: string, data?: any): void {
    log(LogLevel.WARN, message, data);
}
export function logError(message: string, data?: any): void {
    log(LogLevel.ERROR, message, data);
}
