/**
 * Minimal centralized logger. Keeps one app-facing API while supporting
 * readable local logs and machine-readable production logs.
 */
type LogContext = Record<string, unknown>;
type LogLevel = 'debug' | 'info' | 'warn' | 'error';

const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
  debug: 10,
  info: 20,
  warn: 30,
  error: 40,
};

const LOG_LEVEL_COLORS: Record<LogLevel, string> = {
  debug: '\x1b[36m',
  info: '\x1b[32m',
  warn: '\x1b[33m',
  error: '\x1b[31m',
};

const RESET_COLOR = '\x1b[0m';

function readLogLevel(): LogLevel {
  const value = process.env.LOG_LEVEL?.toLowerCase();

  if (value === 'debug' || value === 'info' || value === 'warn' || value === 'error') {
    return value;
  }

  return process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage'
    ? 'info'
    : 'debug';
}

function shouldUseJsonLogs(): boolean {
  if (process.env.LOG_FORMAT === 'json') {
    return true;
  }

  if (process.env.LOG_FORMAT === 'pretty') {
    return false;
  }

  return process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'dev_stage';
}

function shouldLog(level: LogLevel): boolean {
  return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[readLogLevel()];
}

function serializeContext(context?: LogContext): string {
  if (!context || Object.keys(context).length === 0) {
    return '';
  }

  return ` ${JSON.stringify(context)}`;
}

function emit(
  level: LogLevel,
  message: string,
  context?: LogContext,
): void {
  if (!shouldLog(level)) {
    return;
  }

  const time = new Date().toISOString();
  const stream = level === 'error' ? console.error : level === 'warn' ? console.warn : console.log;

  if (shouldUseJsonLogs()) {
    stream(JSON.stringify({
      time,
      level,
      message,
      ...(context && Object.keys(context).length > 0 ? context : {}),
    }));
    return;
  }

  const color = LOG_LEVEL_COLORS[level];
  const line = `${color}${time} ${level.toUpperCase().padEnd(5)}${RESET_COLOR} ${message}${serializeContext(context)}`;

  stream(line);
}

/** Serializes an unknown thrown value into a stable, loggable shape. */
function describeError(error: unknown): LogContext {
  if (error instanceof Error) {
    return { name: error.name, message: error.message, stack: error.stack };
  }
  return { error };
}

const logger = {
  debug(message: string, context?: LogContext): void {
    emit('debug', message, context);
  },
  error(message: string, error?: unknown, context?: LogContext): void {
    emit('error', message, {
      ...(error !== undefined ? describeError(error) : {}),
      ...context,
    });
  },
  warn(message: string, context?: LogContext): void {
    emit('warn', message, context);
  },
  info(message: string, context?: LogContext): void {
    emit('info', message, context);
  },
};

export default logger;
