/**
 * Logger Utility
 *
 * Centralized logging with Winston
 * Provides structured logging with correlation IDs
 */

import winston from 'winston';
import { v4 as uuidv4 } from 'uuid';

/**
 * Log levels
 */
const levels = {
  error: 0,
  warn: 1,
  info: 2,
  http: 3,
  debug: 4,
};

/**
 * Log colors
 */
const colors = {
  error: 'red',
  warn: 'yellow',
  info: 'green',
  http: 'magenta',
  debug: 'white',
};

winston.addColors(colors);

/**
 * Log format
 */
const format = winston.format.combine(
  winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
  winston.format.errors({ stack: true }),
  winston.format.splat(),
  winston.format.json()
);

/**
 * Console format for development
 */
const consoleFormat = winston.format.combine(
  winston.format.colorize({ all: true }),
  winston.format.printf(
    (info) => `${info.timestamp} ${info.level}: ${info.message}` +
      (info.splat !== undefined ? `${info.splat}` : ' ') +
      ((info.error !== undefined || info.stack !== undefined)
        ? `\n${info.stack || info.error}`
        : '')
  )
);

/**
 * Transports
 */
const transports: winston.transport[] = [
  // Console transport
  new winston.transports.Console({
    format: process.env.NODE_ENV === 'production'
      ? format
      : consoleFormat,
  }),

  // Error log file
  new winston.transports.File({
    filename: 'logs/error.log',
    level: 'error',
    format,
  }),

  // Combined log file
  new winston.transports.File({
    filename: 'logs/combined.log',
    format,
  }),
];

/**
 * Winston logger instance
 */
const winstonLogger = winston.createLogger({
  level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
  levels,
  format,
  transports,
  exitOnError: false,
});

/**
 * Universal logger that accepts both strings and objects
 * This is what gets exported as the default logger
 */
type LogMessage = string | Record<string, any>;

interface LoggerInterface {
  error(message: LogMessage, meta?: any): void;
  warn(message: LogMessage, meta?: any): void;
  info(message: LogMessage, meta?: any): void;
  http(message: LogMessage, meta?: any): void;
  debug(message: LogMessage, meta?: any): void;
}

const logger: LoggerInterface = {
  error(message: LogMessage, meta?: any): void {
    if (typeof message === 'string') {
      winstonLogger.error(message, meta);
    } else {
      winstonLogger.error('', message);
    }
  },
  warn(message: LogMessage, meta?: any): void {
    if (typeof message === 'string') {
      winstonLogger.warn(message, meta);
    } else {
      winstonLogger.warn('', message);
    }
  },
  info(message: LogMessage, meta?: any): void {
    if (typeof message === 'string') {
      winstonLogger.info(message, meta);
    } else {
      winstonLogger.info('', message);
    }
  },
  http(message: LogMessage, meta?: any): void {
    if (typeof message === 'string') {
      winstonLogger.http(message, meta);
    } else {
      winstonLogger.http('', message);
    }
  },
  debug(message: LogMessage, meta?: any): void {
    if (typeof message === 'string') {
      winstonLogger.debug(message, meta);
    } else {
      winstonLogger.debug('', message);
    }
  },
};

/**
 * Logger class with correlation ID support
 */
export class Logger {
  private correlationId: string;

  constructor(correlationId?: string) {
    this.correlationId = correlationId || uuidv4();
  }

  /**
   * Log error message
   */
  error(message: string, meta?: any): void;
  error(meta: any): void;
  error(message: string | any, meta?: any): void {
    if (typeof message === 'string') {
      logger.error(message, {
        ...meta,
        correlationId: this.correlationId,
      });
    } else {
      logger.error('', {
        ...message,
        correlationId: this.correlationId,
      });
    }
  }

  /**
   * Log warning message
   */
  warn(message: string, meta?: any): void;
  warn(meta: any): void;
  warn(message: string | any, meta?: any): void {
    if (typeof message === 'string') {
      logger.warn(message, {
        ...meta,
        correlationId: this.correlationId,
      });
    } else {
      logger.warn('', {
        ...message,
        correlationId: this.correlationId,
      });
    }
  }

  /**
   * Log info message
   */
  info(message: string, meta?: any): void;
  info(meta: any): void;
  info(message: string | any, meta?: any): void {
    if (typeof message === 'string') {
      logger.info(message, {
        ...meta,
        correlationId: this.correlationId,
      });
    } else {
      logger.info('', {
        ...message,
        correlationId: this.correlationId,
      });
    }
  }

  /**
   * Log HTTP request
   */
  http(message: string, meta?: any): void;
  http(meta: any): void;
  http(message: string | any, meta?: any): void {
    if (typeof message === 'string') {
      logger.http(message, {
        ...meta,
        correlationId: this.correlationId,
      });
    } else {
      logger.http('', {
        ...message,
        correlationId: this.correlationId,
      });
    }
  }

  /**
   * Log debug message
   */
  debug(message: string, meta?: any): void;
  debug(meta: any): void;
  debug(message: string | any, meta?: any): void {
    if (typeof message === 'string') {
      logger.debug(message, {
        ...meta,
        correlationId: this.correlationId,
      });
    } else {
      logger.debug('', {
        ...message,
        correlationId: this.correlationId,
      });
    }
  }

  /**
   * Get correlation ID
   */
  getCorrelationId(): string {
    return this.correlationId;
  }
}

/**
 * Create a logger instance with a specific correlation ID
 */
export function createLogger(correlationId?: string): Logger {
  return new Logger(correlationId);
}

/**
 * Default logger instance (auto-generates correlation ID)
 */
export { logger };
export default logger;
