/**
 * Structured Logging System
 * Agent 4: Production Deployment
 *
 * Provides structured JSON logging with support for different log levels,
 * context enrichment, and production-ready formatting.
 */

import { env, LogLevel } from '../config';

interface LogContext {
  [key: string]: unknown;
}

interface LogEntry {
  timestamp: string;
  level: LogLevel;
  message: string;
  service: string;
  environment: string;
  context?: LogContext;
  error?: {
    name: string;
    message: string;
    stack?: string;
  };
}

type LogMethod = (message: string, context?: LogContext) => void;

interface Logger {
  error: (message: string, errorOrContext?: Error | LogContext, context?: LogContext) => void;
  warn: LogMethod;
  info: LogMethod;
  debug: LogMethod;
  trace: LogMethod;
  child: (context: LogContext) => Logger;
}

const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
  error: 0,
  warn: 1,
  info: 2,
  debug: 3,
  trace: 4,
};

const LOG_LEVEL_COLORS: Record<LogLevel, string> = {
  error: '\x1b[31m', // Red
  warn: '\x1b[33m',  // Yellow
  info: '\x1b[36m',  // Cyan
  debug: '\x1b[35m', // Magenta
  trace: '\x1b[90m', // Gray
};

const RESET_COLOR = '\x1b[0m';

class LoggerImpl implements Logger {
  private baseContext: LogContext;
  private minLevel: number;
  private format: 'json' | 'pretty';
  private serviceName: string;

  constructor(context: LogContext = {}) {
    this.baseContext = context;
    this.minLevel = LOG_LEVEL_PRIORITY[env.logLevel];
    this.format = env.logFormat;
    this.serviceName = env.appName;
  }

  private shouldLog(level: LogLevel): boolean {
    return LOG_LEVEL_PRIORITY[level] <= this.minLevel;
  }

  private formatEntry(entry: LogEntry): string {
    if (this.format === 'pretty') {
      return this.formatPretty(entry);
    }
    return JSON.stringify(entry);
  }

  private formatPretty(entry: LogEntry): string {
    const color = LOG_LEVEL_COLORS[entry.level];
    const timestamp = new Date(entry.timestamp).toLocaleTimeString();
    const level = entry.level.toUpperCase().padEnd(5);

    let output = `${color}[${timestamp}] ${level}${RESET_COLOR} ${entry.message}`;

    if (entry.context && Object.keys(entry.context).length > 0) {
      output += ` ${JSON.stringify(entry.context)}`;
    }

    if (entry.error) {
      output += `\n  ${color}Error: ${entry.error.message}${RESET_COLOR}`;
      if (entry.error.stack) {
        output += `\n${entry.error.stack.split('\n').slice(1).join('\n')}`;
      }
    }

    return output;
  }

  private log(level: LogLevel, message: string, context?: LogContext, error?: Error): void {
    if (!this.shouldLog(level)) {
      return;
    }

    const entry: LogEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      service: this.serviceName,
      environment: env.nodeEnv,
    };

    // Merge base context with provided context
    const mergedContext = { ...this.baseContext, ...context };
    if (Object.keys(mergedContext).length > 0) {
      entry.context = mergedContext;
    }

    // Add error details if present
    if (error) {
      entry.error = {
        name: error.name,
        message: error.message,
        stack: error.stack,
      };
    }

    const output = this.formatEntry(entry);

    // Use appropriate console method
    switch (level) {
      case 'error':
        console.error(output);
        break;
      case 'warn':
        console.warn(output);
        break;
      case 'debug':
      case 'trace':
        console.debug(output);
        break;
      default:
        console.log(output);
    }
  }

  error(message: string, errorOrContext?: Error | LogContext, context?: LogContext): void {
    if (errorOrContext instanceof Error) {
      this.log('error', message, context, errorOrContext);
    } else {
      this.log('error', message, errorOrContext);
    }
  }

  warn(message: string, context?: LogContext): void {
    this.log('warn', message, context);
  }

  info(message: string, context?: LogContext): void {
    this.log('info', message, context);
  }

  debug(message: string, context?: LogContext): void {
    this.log('debug', message, context);
  }

  trace(message: string, context?: LogContext): void {
    this.log('trace', message, context);
  }

  child(context: LogContext): Logger {
    return new LoggerImpl({ ...this.baseContext, ...context });
  }
}

// Create and export default logger instance
export const logger = new LoggerImpl();

// Export for creating child loggers with context
export function createLogger(context: LogContext): Logger {
  return new LoggerImpl(context);
}

// Export types
export type { Logger, LogContext, LogEntry };
