import { Module, RequestMethod } from '@nestjs/common';
import { LoggerModule } from 'pino-nestjs';
import { join } from 'path';
import { mkdirSync } from 'fs';
import pino from 'pino';
import pretty from 'pino-pretty';

// const isDevEnv = process.env.NODE_ENV === 'development' || !process.env.NODE_ENV;

/**
 * A highly optimized daily rotating file stream that uses Pino's native,
 * highest-performance asynchronous destination stream (SonicBoom).
 * Generates file names matching exactly `app-YYYY-MM-DD.log` without the
 * extra sequence/index numbers (e.g., `.1.`).
 */
class DailyRotateStream {
  private currentPath: string = '';
  private dest: any = null;
  private logDir: string;
  private prefix: string;

  constructor(logDir: string, prefix: string = 'app') {
    this.logDir = logDir;
    this.prefix = prefix;
    mkdirSync(logDir, { recursive: true });
    this.rotate();
  }

  private getTodayString(): string {
    return new Date().toISOString().split('T')[0];
  }

  private rotate() {
    const today = this.getTodayString();
    const newPath = join(this.logDir, `${this.prefix}-${today}.log`);

    if (this.currentPath !== newPath) {
      if (this.dest) {
        this.dest.end(); // close previous file stream
      }
      this.currentPath = newPath;
      this.dest = pino.destination({
        dest: newPath,
        sync: false, // high-speed non-blocking asynchronous writing
      });
    }
  }

  // Writable interface expected by Pino
  write(chunk: string) {
    this.rotate();
    try {
      const logObj = JSON.parse(chunk.trim());
      const { time, level, message, context, ...rest } = logObj;

      const orderedLog: Record<string, any> = {};

      if (time !== undefined) {
        orderedLog.time = time;
      }
      if (level !== undefined) {
        orderedLog.level = level;
      }
      if (message !== undefined) {
        orderedLog.message = message;
      }
      if (context !== undefined) {
        orderedLog.context = context;
      }

      const finalLog = {
        ...orderedLog,
        ...rest,
      };

      this.dest.write(JSON.stringify(finalLog) + '\n');
    } catch (e) {
      // Fallback in case of raw non-JSON stream chunks
      this.dest.write(chunk);
    }
  }
}

// Instantiate daily log writer
const fileLogStream = new DailyRotateStream(join(process.cwd(), 'logs'), 'app');

// Dynamic streams setup based on environment
const streams: pino.StreamEntry[] = [
  {
    stream: pretty({
      colorize: true,
      singleLine: true,
      translateTime: 'SYS:yyyy-MM-dd HH:mm:ss.l',
      ignore: 'pid,hostname',
    }),
  },
  {
    stream: fileLogStream,
  },
];

@Module({
  imports: [
    LoggerModule.forRoot({
      forRoutes: [{ method: RequestMethod.ALL, path: '*splat' }],
      pinoHttp: {
        autoLogging: false,
        redact: ['req.body.password'],
        stream: pino.multistream(streams),
        base: undefined,
        timestamp: () => `,"time":"${new Date().toISOString().replace('T', ' ').slice(0, 19)}"`,
        messageKey: 'message',
        serializers: {
          req: (req) => ({ method: req.method, url: req.url }),
          res: (res) => ({ statusCode: res.statusCode }),
        },
        hooks: {
          logMethod(inputArgs: any, method) {
            // Under pino-nestjs, inputArgs[0] is the context object: { context: '...' }
            const context = inputArgs[0]?.context;
            const ignoredContexts = ['InstanceLoader', 'RoutesResolver', 'RouterExplorer'];
            if (ignoredContexts.includes(context)) {
              return; // Mute noisy NestJS startup logs
            }

            return method.apply(this, inputArgs);
          },
        },
        formatters: {
          level: (label: string) => ({ level: label.toUpperCase() }),
          bindings: () => ({}),
          log(object) {
            const { message, context, ...rest } = object;
            const logObj: Record<string, any> = {};
            if (message !== undefined) {
              logObj.message = message;
            }
            logObj.context = context || 'App';
            return {
              ...logObj,
              ...rest,
            };
          },
        },
      },
    }),
  ],
  exports: [LoggerModule],
})
export class LogModule {}
