import pino from 'pino';
import * as path from 'path';
import * as fs from 'fs';

export enum LogType {
  FAILED = 'FAILED',
  WARN = 'WARN',
  DEBUG = 'DEBUG',
  SUCCESS = 'SUCCESS',
  STARTED = 'STARTED',
  FINISHED = 'FINISHED',
  INFO = 'INFO',
}

export class WingLog {
  private name: string;
  private logger: pino.Logger;

  constructor(name: string) {
    this.name = name;
    
    // Ensure logs directory exists
    const logsDir = path.join(process.cwd(), 'logs');
    if (!fs.existsSync(logsDir)) {
      fs.mkdirSync(logsDir, { recursive: true });
    }

    const logFile = path.join(logsDir, `${name}.log`);

    const createConsoleTransport = () => {
      try {
        return pino.transport({
          target: 'pino-pretty',
          options: {
            colorize: true,
            translateTime: 'SYS:standard',
            ignore: 'pid,hostname',
            messageFormat: '{msg}',
          },
        });
      } catch (error) {
        return pino.destination(1); // stdout
      }
    };

    this.logger = pino({
      level: 'debug',
      timestamp: () => `,"time":"${new Date().toISOString()}"`,
    }, pino.multistream([
      {
        level: 'debug',
        stream: createConsoleTransport(),
      },
      {
        level: 'debug',
        stream: pino.destination({
          dest: logFile,
          sync: false,
          mkdir: true,
        }),
      },
    ]));

    this.logger.debug(`Logger initialized: [${this.name}]`);
  }

  private formatDuration(seconds: number): string {
    const mins = Math.floor(seconds / 60);
    let secs = Math.round(seconds % 60);
    if (secs === 60) {
      secs = 0;
    }
    return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  }

  private log(message: string, type: LogType | '', icon: string, startTime?: number): number {
    let timeSpent = 0;
    let duration = '';
    icon = icon || '';

    if (startTime) {
      timeSpent = (Date.now() - startTime) / 1000;
      duration = `Duration:[${this.formatDuration(timeSpent)}]:`;
    }

    let logMessage = `[${this.name}]:${duration}${message}`;

    switch (type) {
      case LogType.FAILED:
        this.logger.error(logMessage);
        break;
      case LogType.WARN:
        this.logger.warn(logMessage);
        break;
      case LogType.DEBUG:
        this.logger.debug(logMessage);
        break;
      case LogType.SUCCESS:
      case LogType.STARTED:
      case LogType.FINISHED:
      case LogType.INFO:
      default:
        this.logger.info(logMessage);
        break;
    }

    return Number(timeSpent.toFixed(2));
  }

  private logWithData(message: string, type: LogType, rec: Record<string, any>): void {
    this.log(`${message} ${JSON.stringify(rec)}`, type, '', 0); 
  }

  // Method overloads for started
  started(message: string, startTime?: number): number;
  started(message: string, rec: Record<string, any>): void;
  started(message: string, startTimeOrRec?: number | Record<string, any>): number | void {
    if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') {
      return this.log(message, LogType.STARTED, '>', startTimeOrRec);
    } else {
      this.logWithData(message, LogType.STARTED, startTimeOrRec);
    }
  }

  // Method overloads for finished
  finished(message: string, startTime?: number): number;
  finished(message: string, rec: Record<string, any>): void;
  finished(message: string, startTimeOrRec?: number | Record<string, any>): number | void {
    if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') {
      return this.log(message, LogType.FINISHED, '✓', startTimeOrRec);
    } else {
      this.logWithData(message, LogType.FINISHED, startTimeOrRec);
    }
  }

  // Method overloads for success
  success(message: string, startTime?: number): number;
  success(message: string, rec: Record<string, any>): void;
  success(message: string, startTimeOrRec?: number | Record<string, any>): number | void {
    if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') {
      return this.log(message, LogType.SUCCESS, '+', startTimeOrRec);
    } else {
      this.logWithData(message, LogType.SUCCESS, startTimeOrRec);
    }
  }

  // Method overloads for failed
  failed(message: string, startTime?: number): number;
  failed(message: string, rec: Record<string, any>): void;
  failed(message: string, startTimeOrRec?: number | Record<string, any>): number | void {
    if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') {
      return this.log(message, LogType.FAILED, 'x', startTimeOrRec);
    } else {
      this.logWithData(message, LogType.FAILED, startTimeOrRec);
    }
  }

  // Method overloads for info
  info(message: string, startTime?: number): number;
  info(message: string, rec: Record<string, any>): void;
  info(message: string, startTimeOrRec?: number | Record<string, any>): number | void {
    if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') {
      return this.log(message, LogType.INFO, 'i', startTimeOrRec);
    } else {
      this.logWithData(message, LogType.INFO, startTimeOrRec);
    }
  }
  
  // Method overloads for warn
  warn(message: string, startTime?: number): number;
  warn(message: string, rec: Record<string, any>): void;
  warn(message: string, startTimeOrRec?: number | Record<string, any>): number | void {
    if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') {
      return this.log(message, LogType.WARN, '', startTimeOrRec);
    } else {
      this.logWithData(message, LogType.WARN, startTimeOrRec);
    }
  }
    
  // Method overloads for debug
  debug(message: string, startTime?: number): number;
  debug(message: string, rec: Record<string, any>): void;
  debug(message: string, startTimeOrRec?: number | Record<string, any>): number | void {
    if (startTimeOrRec === undefined || typeof startTimeOrRec === 'number') {
      return this.log(message, LogType.DEBUG, '', startTimeOrRec);
    } else {
      this.logWithData(message, LogType.DEBUG, startTimeOrRec);
    }
  }

  error(message: string, err: unknown): void {
    let error = err instanceof Error ? err : new Error(String(err))
    let msg = `${message}: ${error.message}`;
    let rec = {"error": error};

    this.logWithData(msg, LogType.FAILED, rec);
  }
}

export { WingLog as Logger }; 