/**
 * Frontend Logger
 *
 * A lightweight, isomorphic logger for Next.js applications.
 * Works in both browser and SSR environments.
 *
 * Usage:
 *   import { logger } from '@/lib/logger';
 *   logger.info('User logged in', { userId: '123' });
 *   logger.error('Failed to fetch data', error);
 */

type LogLevel = 'debug' | 'info' | 'warn' | 'error';

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

interface LoggerConfig {
  level: LogLevel;
  isDevelopment: boolean;
  service: string;
}

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

class Logger {
  private config: LoggerConfig;

  constructor() {
    this.config = {
      level: (process.env.NEXT_PUBLIC_LOG_LEVEL as LogLevel) || 'info',
      isDevelopment: process.env.NODE_ENV === 'development',
      service: 'tour-builder-frontend',
    };
  }

  private shouldLog(level: LogLevel): boolean {
    return LOG_LEVELS[level] >= LOG_LEVELS[this.config.level];
  }

  private formatMessage(
    level: LogLevel,
    message: string,
    context?: LogContext | Error,
  ): { formatted: string; data?: object } {
    const timestamp = new Date().toISOString();
    const prefix = `[${timestamp}] [${level.toUpperCase()}]`;

    if (context instanceof Error) {
      return {
        formatted: `${prefix} ${message}`,
        data: {
          error: context.message,
          stack: context.stack,
        },
      };
    }

    return {
      formatted: `${prefix} ${message}`,
      data: context,
    };
  }

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

    const { formatted, data } = this.formatMessage(level, message, context);

    // In development, use colored console output
    if (this.config.isDevelopment) {
      const consoleFn = level === 'debug' ? 'log' : level;
      if (data) {
        console[consoleFn](formatted, data);
      } else {
        console[consoleFn](formatted);
      }
      return;
    }

    // In production, use structured JSON logging (for log aggregation services)
    const logEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      service: this.config.service,
      ...(data || {}),
    };

    const consoleFn = level === 'debug' ? 'log' : level;
    console[consoleFn](JSON.stringify(logEntry));
  }

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

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

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

  error(message: string, context?: LogContext | Error): void {
    this.output('error', message, context);
  }

  /**
   * Create a child logger with additional context
   */
  child(context: LogContext): ChildLogger {
    return new ChildLogger(this, context);
  }
}

class ChildLogger {
  constructor(
    private parent: Logger,
    private context: LogContext,
  ) {}

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

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

  warn(message: string, additionalContext?: LogContext | Error): void {
    if (additionalContext instanceof Error) {
      this.parent.warn(message, additionalContext);
    } else {
      this.parent.warn(message, { ...this.context, ...additionalContext });
    }
  }

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

// Export singleton instance
export const logger = new Logger();

// Export types for consumers
export type { LogLevel, LogContext };
