/**
 * 日志配置模块
 *
 * 功能描述：
 * - 提供详细的日志配置选项
 * - 支持日志文件轮转和管理
 * - 根据环境自动调整日志策略
 * - 提供日志文件清理和归档功能
 *
 * 职责分离：
 * - 配置生成：根据环境变量生成Pino日志配置
 * - 文件管理：管理日志文件的创建和轮转
 * - 策略适配：提供不同环境的日志输出策略
 * - 目录维护：确保日志目录存在和可访问
 *
 * 最近修改：
 * - 2026-01-07: 代码规范优化 - 完善注释文档和配置说明
 *
 * @author moyin
 * @version 1.0.1
 * @since 2025-12-13
 * @lastModified 2026-01-07
 */

import { ConfigService } from '@nestjs/config';
import * as path from 'path';
import * as fs from 'fs';

/**
 * 日志配置工厂类
 *
 * 职责：
 * - 根据环境变量生成 Pino 日志配置
 * - 管理日志文件的创建和轮转
 * - 提供不同环境的日志策略
 */
export class LoggerConfigFactory {
  /**
   * 创建 Pino 日志配置
   *
   * 功能描述：
   * 根据环境变量和配置生成完整的 Pino 日志配置对象
   *
   * 业务逻辑：
   * 1. 读取环境变量配置
   * 2. 确保日志目录存在
   * 3. 根据环境选择不同的输出策略
   * 4. 配置日志轮转和清理策略
   *
   * @param configService 配置服务实例
   * @returns Pino 日志配置对象
   */
  static createLoggerConfig(configService: ConfigService) {
    const isProduction = configService.get('NODE_ENV') === 'production';
    const logDir = configService.get('LOG_DIR', './logs');
    const logLevel = configService.get('LOG_LEVEL', isProduction ? 'info' : 'debug');
    const appName = configService.get('APP_NAME', 'pixel-game-server');

    // 确保日志目录存在
    this.ensureLogDirectory(logDir);

    return {
      pinoHttp: {
        level: logLevel,

        // 根据环境配置不同的输出策略
        transport: this.createTransportConfig(isProduction, logDir, logLevel),

        // 自定义序列化器
        serializers: this.createSerializers(),

        // 基础字段
        base: {
          pid: process.pid,
          hostname: require('os').hostname(),
          app: appName,
          version: process.env.npm_package_version || '1.0.0',
        },

        // HTTP 请求日志配置
        autoLogging: true,

        // 自定义日志级别判断
        customLogLevel: this.customLogLevel,

        // 自定义请求ID生成
        genReqId: (req: any) => req.headers['x-request-id'] || this.generateRequestId(),

        // 自定义成功响应消息
        customSuccessMessage: (req: any, res: any) => {
          return `${req.method} ${req.url} completed in ${res.responseTime}ms`;
        },

        // 自定义错误响应消息
        customErrorMessage: (req: any, _res: any, err: any) => {
          return `${req.method} ${req.url} failed: ${err.message}`;
        },
      },
    };
  }

  /**
   * 创建传输配置
   *
   * @param isProduction 是否为生产环境
   * @param logDir 日志目录
   * @param logLevel 日志级别
   * @returns 传输配置对象
   * @private
   */
  private static createTransportConfig(isProduction: boolean, logDir: string, logLevel: string) {
    if (isProduction) {
      // 生产环境：多目标输出，包含日志轮转
      return {
        targets: [
          {
            // 应用日志（所有级别）
            target: 'pino/file',
            options: {
              destination: path.join(logDir, 'app.log'),
              mkdir: true,
            },
            level: 'info',
          },
          {
            // 错误日志（仅错误和致命错误）
            target: 'pino/file',
            options: {
              destination: path.join(logDir, 'error.log'),
              mkdir: true,
            },
            level: 'error',
          },
          {
            // 访问日志（HTTP 请求）
            target: 'pino/file',
            options: {
              destination: path.join(logDir, 'access.log'),
              mkdir: true,
            },
            level: 'info',
          },
          {
            // 控制台输出（用于容器日志收集）
            target: 'pino/file',
            options: {
              destination: 1, // stdout
            },
            level: 'warn',
          },
        ],
      };
    } else {
      // 开发环境：美化输出 + 文件备份
      return {
        targets: [
          {
            // 控制台美化输出
            target: 'pino-pretty',
            options: {
              colorize: true,
              translateTime: 'SYS:yyyy-mm-dd HH:MM:ss',
              ignore: 'pid,hostname',
              messageFormat: '{app} [{level}] {msg}',
              // 移除 customPrettifiers 以避免 Worker 线程序列化问题
            },
            level: logLevel,
          },
          {
            // 开发环境文件输出
            target: 'pino/file',
            options: {
              destination: path.join(logDir, 'dev.log'),
              mkdir: true,
            },
            level: 'debug',
          },
        ],
      };
    }
  }

  /**
   * 创建序列化器配置
   *
   * @returns 序列化器配置对象
   * @private
   */
  private static createSerializers() {
    return {
      req: (req: any) => ({
        id: req.id,
        method: req.method,
        url: req.url,
        path: req.route?.path,
        parameters: req.params,
        query: req.query,
        headers: {
          host: req.headers.host,
          'user-agent': req.headers['user-agent'],
          'content-type': req.headers['content-type'],
          'content-length': req.headers['content-length'],
          authorization: req.headers.authorization ? '[REDACTED]' : undefined,
        },
        ip: req.ip,
        ips: req.ips,
        hostname: req.hostname,
      }),
      res: (res: any) => ({
        statusCode: res.statusCode,
        statusMessage: res.statusMessage,
        headers: {
          'content-type': res.getHeader ? res.getHeader('content-type') : res.headers?.['content-type'],
          'content-length': res.getHeader ? res.getHeader('content-length') : res.headers?.['content-length'],
        },
        responseTime: res.responseTime,
      }),
      err: (err: any) => ({
        type: err.constructor.name,
        message: err.message,
        stack: err.stack,
        code: err.code,
        statusCode: err.statusCode,
      }),
    };
  }

  /**
   * 自定义日志级别判断
   *
   * @param _req HTTP 请求对象
   * @param res HTTP 响应对象
   * @param err 错误对象
   * @returns 日志级别
   * @private
   */
  private static customLogLevel(_req: any, res: any, err: any) {
    if (res.statusCode >= 400 && res.statusCode < 500) {
      return 'warn';
    } else if (res.statusCode >= 500 || err) {
      return 'error';
    } else if (res.statusCode >= 300 && res.statusCode < 400) {
      return 'info';
    }
    return 'info';
  }

  /**
   * 生成请求ID
   *
   * @returns 唯一的请求ID
   * @private
   */
  private static generateRequestId(): string {
    return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
  }

  /**
   * 确保日志目录存在
   *
   * @param logDir 日志目录路径
   * @private
   */
  private static ensureLogDirectory(logDir: string): void {
    try {
      if (!fs.existsSync(logDir)) {
        fs.mkdirSync(logDir, { recursive: true });
        console.log(`📁 Created log directory: ${logDir}`);
      }
    } catch (error) {
      console.error(`❌ Failed to create log directory: ${logDir}`, error);
      throw error;
    }
  }
}