import { ConsoleLogger, Injectable, Scope } from '@nestjs/common';
import { join } from 'path';
import pino, { type Logger } from 'pino';
import pinoPretty from 'pino-pretty';
import dayjs from 'dayjs';
import { execSync } from 'child_process';

/**
 * 检测终端是否支持 UTF-8 编码
 * @returns 是否支持 UTF-8
 */
function isTerminalUTF8(): boolean {
  // Windows 系统检查代码页
  if (process.platform === 'win32') {
    try {
      // 检查是否设置了 NODE_SKIP_UTF8_CHECK 环境变量
      if (process.env.NODE_SKIP_UTF8_CHECK) {
        return false;
      }

      // 尝试执行 chcp 命令检查代码页
      const codePage = execSync('chcp', { encoding: 'utf8' }).toString();
      // 65001 是 UTF-8 代码页
      return codePage.includes('65001');
    } catch {
      // 默认 Windows 终端使用 GBK 编码
      return false;
    }
  }

  // macOS 和 Linux 通常默认使用 UTF-8
  return true;
}

/**
 * 设置终端为 UTF-8 编码（仅 Windows）
 */
function setTerminalToUTF8(): void {
  if (process.platform === 'win32' && !isTerminalUTF8()) {
    try {
      // 设置 stdout 为 UTF-8
      if (process.stdout && typeof process.stdout.setEncoding === 'function') {
        process.stdout.setEncoding('utf-8');
      }
    } catch {
      // 忽略错误
    }
  }
}

/**
 * 日志服务.
 */
@Injectable({ scope: Scope.TRANSIENT })
export class LoggerService extends ConsoleLogger {
  /** 日志实例 */
  public logger: Logger | undefined = undefined;

  /** 上下文 */
  public override context = '';

  constructor(context?: string) {
    super(context || '');

    // 设置终端编码
    setTerminalToUTF8();

    if (process.env.NODE_ENV === 'development') {
      const prettyStream = pinoPretty({
        colorize: true,
        colorizeObjects: true,
        singleLine: true,
        translateTime: 'SYS:yyyy-mm-dd HH:MM:ss.l',
        // 根据终端编码设置输出
        sync: true,
        customPrettifiers: {
          /** 自定义 err 的显示 */
          err: (err: unknown) => {
            return `\x1b[31m${JSON.stringify(err, null, 2)}\x1b[0m`;
          },

          /** 自定义 err 的显示 */
          error: (err: unknown) => {
            return `\x1b[31m${JSON.stringify(err, null, 2)}\x1b[0m`;
          },
        },
      });
      this.logger = pino(prettyStream);
    } else {
      this.logger = pino({
        // level: 'warn',

        /** 处理时间字段 */
        timestamp: () => `,"time":"${dayjs().format('YYYY-MM-DD HH:mm:ss.SSS')}"`,
        transport: {
          target: 'pino-roll',
          options: {
            file: join('logs', 'log'), // 日志文件的绝对或相对路径
            size: '40m', // 日志文件的最大大小
            dateFormat: 'yyyy-MM-dd',
            frequency: 'daily', // 周期
            mkdir: true,
            extension: `.log`,
          },
        },
      });
    }
  }

  /**
   * 设置日志上下文.
   */
  override setContext(context: string) {
    this.context = context;
  }

  /**
   * Info级别日志.
   */
  info(obj: any, msg?: string, ...args: any[]) {
    this.logger!.info(obj, msg, ...args, this.context);
  }

  /**
   * Error级别日志.
   */
  override error(obj: any, msg?: string, ...args: any[]) {
    this.logger!.error(obj, msg, ...args, this.context);
  }

  /**
   * Warn级别日志.
   */
  override warn(obj: any, msg?: string, ...args: any[]) {
    this.logger!.warn(obj, msg, ...args, this.context);
  }

  /**
   * Debug级别日志.
   */
  override debug(obj: any, msg?: string, ...args: any[]) {
    this.logger!.debug(obj, msg, ...args, this.context);
  }

  /**
   * Trace级别日志.
   */
  trace(obj: any, msg?: string, ...args: any[]) {
    this.logger!.trace(obj, msg, ...args, this.context);
  }

  /**
   * Fatal级别日志
   * 由于 'fatal' 级别的消息旨在在退出进程之前记录，因此 fatal 方法将始终同步刷新目标。因此，重要的是不要滥用 fatal，因为如果将其用于进程崩溃或退出之前写入最终日志消息之外的任何其他目的，则会导致性能开销。.
   */
  override fatal(obj: any, msg?: string, ...args: any[]) {
    this.logger!.fatal(obj, msg, ...args, this.context);
  }
}
