/**
 * 日志管理服务
 * 
 * 功能描述：
 * - 定期清理过期日志文件
 * - 监控日志文件大小和数量
 * - 提供日志统计和分析功能
 * - 支持日志文件压缩和归档
 * 
 * 职责分离：
 * - 定时清理：执行定期日志文件清理任务
 * - 健康监控：监控日志系统运行状态
 * - 统计分析：提供日志文件统计和分析数据
 * - 生命周期管理：管理日志文件的完整生命周期
 * 
 * 最近修改：
 * - 2026-01-07: 代码规范优化 - 修复常量命名规范(LOG_DIR, MAX_FILES, MAX_SIZE)，清理未使用导入(zlib)
 * 
 * 依赖模块：
 * - ConfigService: 环境配置服务
 * - AppLoggerService: 应用日志服务
 * - ScheduleModule: 定时任务模块
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2025-12-13
 * @lastModified 2026-01-07
 */

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cron, CronExpression } from '@nestjs/schedule';
import { AppLoggerService } from './logger.service';
import * as fs from 'fs';
import * as path from 'path';

/**
 * 日志管理服务类
 * 
 * 职责：
 * - 执行定期日志清理任务
 * - 监控日志系统健康状态
 * - 提供日志文件统计信息
 * - 管理日志文件的生命周期
 * 
 * 主要方法：
 * - cleanupOldLogs(): 清理过期日志文件
 * - compressLogs(): 压缩历史日志文件
 * - getLogStatistics(): 获取日志统计信息
 * - monitorLogHealth(): 监控日志系统健康状态
 * 
 * 使用场景：
 * - 定期维护日志文件
 * - 监控系统日志状态
 * - 优化存储空间使用
 * - 提供日志分析数据
 */
@Injectable()
export class LogManagementService {
  private readonly LOG_DIR: string;
  private readonly MAX_FILES: number;
  private readonly MAX_SIZE: string;

  constructor(
    private readonly configService: ConfigService,
    private readonly logger: AppLoggerService,
  ) {
    this.LOG_DIR = this.configService.get('LOG_DIR', './logs');
    this.MAX_FILES = this.parseMaxFiles(this.configService.get('LOG_MAX_FILES', '7d'));
    this.MAX_SIZE = this.configService.get('LOG_MAX_SIZE', '10m');
  }

  /**
   * 获取日志目录的绝对路径
   *
   * 说明：用于后台打包下载 logs/ 整目录。
   */
  getLogDirAbsolutePath(): string {
    return path.resolve(this.LOG_DIR);
  }

  /**
   * 定期清理过期日志文件
   * 
   * 功能描述：
   * 每天凌晨2点执行，清理超过保留期限的日志文件
   * 
   * 业务逻辑：
   * 1. 扫描日志目录中的所有文件
   * 2. 检查文件创建时间
   * 3. 删除超过保留期限的文件
   * 4. 记录清理结果
   * 
   * @cron 每天凌晨2点执行
   */
  @Cron('0 2 * * *', {
    name: 'cleanup-old-logs',
    timeZone: 'Asia/Shanghai',
  })
  async cleanupOldLogs(): Promise<void> {
    const startTime = Date.now();
    
    this.logger.info('开始执行日志清理任务', {
      operation: 'cleanupOldLogs',
      logDir: this.LOG_DIR,
      maxFiles: this.MAX_FILES,
      timestamp: new Date().toISOString(),
    });

    try {
      if (!fs.existsSync(this.LOG_DIR)) {
        this.logger.warn('日志目录不存在，跳过清理任务', {
          operation: 'cleanupOldLogs',
          logDir: this.LOG_DIR,
        });
        return;
      }

      const files = fs.readdirSync(this.LOG_DIR);
      const cutoffDate = new Date();
      cutoffDate.setDate(cutoffDate.getDate() - this.MAX_FILES);

      let deletedCount = 0;
      let deletedSize = 0;

      for (const file of files) {
        const filePath = path.join(this.LOG_DIR, file);
        const stats = fs.statSync(filePath);

        // 只处理日志文件（.log 扩展名）
        if (path.extname(file) === '.log' && stats.birthtime < cutoffDate) {
          try {
            deletedSize += stats.size;
            fs.unlinkSync(filePath);
            deletedCount++;

            this.logger.info('删除过期日志文件', {
              operation: 'cleanupOldLogs',
              fileName: file,
              fileSize: this.formatBytes(stats.size),
              fileAge: Math.floor((Date.now() - stats.birthtime.getTime()) / (1000 * 60 * 60 * 24)),
            });
          } catch (error) {
            this.logger.error('删除日志文件失败', {
              operation: 'cleanupOldLogs',
              fileName: file,
              error: error instanceof Error ? error.message : String(error),
            }, error instanceof Error ? error.stack : undefined);
          }
        }
      }

      const duration = Date.now() - startTime;

      this.logger.info('日志清理任务完成', {
        operation: 'cleanupOldLogs',
        deletedCount,
        deletedSize: this.formatBytes(deletedSize),
        duration,
        timestamp: new Date().toISOString(),
      });

    } catch (error) {
      const duration = Date.now() - startTime;
      
      this.logger.error('日志清理任务执行失败', {
        operation: 'cleanupOldLogs',
        logDir: this.LOG_DIR,
        error: error instanceof Error ? error.message : String(error),
        duration,
        timestamp: new Date().toISOString(),
      }, error instanceof Error ? error.stack : undefined);
    }
  }

  /**
   * 定期压缩历史日志文件
   * 
   * 功能描述：
   * 每周日凌晨3点执行，压缩7天前的日志文件以节省存储空间
   * 
   * @cron 每周日凌晨3点执行
   */
  @Cron('0 3 * * 0', {
    name: 'compress-logs',
    timeZone: 'Asia/Shanghai',
  })
  async compressLogs(): Promise<void> {
    this.logger.info('日志压缩任务已跳过', {
      operation: 'compressLogs',
      reason: '使用简化的日志管理策略',
      timestamp: new Date().toISOString(),
    });
    
    // 简化版本：只记录任务执行，实际压缩功能可以后续添加
    // 这样可以避免复杂的文件操作导致的问题
  }

  /**
   * 定期监控日志系统健康状态
   * 
   * 功能描述：
   * 每小时执行一次，检查日志系统的健康状态
   * 
   * @cron 每小时执行
   */
  @Cron(CronExpression.EVERY_HOUR, {
    name: 'monitor-log-health',
  })
  async monitorLogHealth(): Promise<void> {
    try {
      const stats = await this.getLogStatistics();
      
      // 检查磁盘空间使用情况
      if (stats.totalSize > this.parseSize(this.MAX_SIZE) * 100) { // 如果总大小超过单文件限制的100倍
        this.logger.warn('日志文件占用空间过大', {
          operation: 'monitorLogHealth',
          totalSize: this.formatBytes(stats.totalSize),
          fileCount: stats.fileCount,
          recommendation: '建议检查日志清理策略',
        });
      }

      // 检查错误日志数量
      if (stats.errorLogCount > 1000) { // 如果错误日志过多
        this.logger.warn('错误日志数量异常', {
          operation: 'monitorLogHealth',
          errorLogCount: stats.errorLogCount,
          recommendation: '建议检查系统运行状态',
        });
      }

      // 定期输出日志统计信息（每天一次）
      const hour = new Date().getHours();
      if (hour === 9) { // 每天上午9点输出统计信息
        this.logger.info('日志系统健康状态报告', {
          operation: 'monitorLogHealth',
          ...stats,
          timestamp: new Date().toISOString(),
        });
      }

    } catch (error) {
      this.logger.error('日志健康监控失败', {
        operation: 'monitorLogHealth',
        error: error instanceof Error ? error.message : String(error),
        timestamp: new Date().toISOString(),
      }, error instanceof Error ? error.stack : undefined);
    }
  }

  /**
   * 获取日志统计信息
   * 
   * 功能描述：
   * 统计日志目录中的文件数量、大小等信息
   * 
   * @returns 日志统计信息对象
   */
  async getLogStatistics(): Promise<{
    fileCount: number;
    totalSize: number;
    errorLogCount: number;
    oldestFile: string;
    newestFile: string;
    avgFileSize: number;
  }> {
    try {
      if (!fs.existsSync(this.LOG_DIR)) {
        return {
          fileCount: 0,
          totalSize: 0,
          errorLogCount: 0,
          oldestFile: '',
          newestFile: '',
          avgFileSize: 0,
        };
      }

      const files = fs.readdirSync(this.LOG_DIR);
      let totalSize = 0;
      let errorLogCount = 0;
      let oldestTime = Date.now();
      let newestTime = 0;
      let oldestFile = '';
      let newestFile = '';

      for (const file of files) {
        const filePath = path.join(this.LOG_DIR, file);
        const stats = fs.statSync(filePath);
        
        totalSize += stats.size;
        
        if (file.includes('error')) {
          errorLogCount++;
        }

        if (stats.birthtime.getTime() < oldestTime) {
          oldestTime = stats.birthtime.getTime();
          oldestFile = file;
        }

        if (stats.birthtime.getTime() > newestTime) {
          newestTime = stats.birthtime.getTime();
          newestFile = file;
        }
      }

      return {
        fileCount: files.length,
        totalSize,
        errorLogCount,
        oldestFile,
        newestFile,
        avgFileSize: files.length > 0 ? Math.round(totalSize / files.length) : 0,
      };

    } catch (error) {
      this.logger.error('获取日志统计信息失败', {
        operation: 'getLogStatistics',
        error: error instanceof Error ? error.message : String(error),
      }, error instanceof Error ? error.stack : undefined);
      
      throw error;
    }
  }

  /**
   * 获取运行日志尾部（用于后台查看）
   *
   * 说明：
   * - 开发环境默认读取 dev.log
   * - 生产环境默认读取 app.log（可选 access/error）
   * - 通过读取文件尾部一定字节数实现“近似 tail”，避免大文件全量读取
   */
  async getRuntimeLogTail(options?: {
    type?: 'app' | 'access' | 'error' | 'dev';
    lines?: number;
  }): Promise<{
    file: string;
    updated_at: string;
    lines: string[];
  }> {
    const isProduction = this.configService.get('NODE_ENV') === 'production';
    const requestedLines = Math.max(1, Math.min(Number(options?.lines ?? 200), 2000));
    const requestedType = options?.type;

    const allowedFiles = isProduction
      ? {
          app: 'app.log',
          access: 'access.log',
          error: 'error.log',
        }
      : {
          dev: 'dev.log',
        };

    const defaultType = isProduction ? 'app' : 'dev';
    const typeKey = (requestedType && requestedType in allowedFiles ? requestedType : defaultType) as keyof typeof allowedFiles;
    const fileName = allowedFiles[typeKey];
    const filePath = path.join(this.LOG_DIR, fileName);

    if (!fs.existsSync(filePath)) {
      return { file: fileName, updated_at: new Date().toISOString(), lines: [] };
    }

    const stats = fs.statSync(filePath);
    const maxBytes = 256 * 1024; // 256KB 足够覆盖常见的数百行日志
    const readBytes = Math.min(stats.size, maxBytes);
    const startPos = Math.max(0, stats.size - readBytes);

    const fd = fs.openSync(filePath, 'r');
    try {
      const buffer = Buffer.alloc(readBytes);
      fs.readSync(fd, buffer, 0, readBytes, startPos);
      const text = buffer.toString('utf8');
      const allLines = text.split(/\r?\n/).filter((l) => l.length > 0);
      const tailLines = allLines.slice(-requestedLines);
      return {
        file: fileName,
        updated_at: stats.mtime.toISOString(),
        lines: tailLines,
      };
    } finally {
      fs.closeSync(fd);
    }
  }

  /**
   * 解析最大文件数配置
   * 
   * @param maxFiles 配置字符串（如 "7d", "30", "2w"）
   * @returns 天数
   * @private
   */
  private parseMaxFiles(maxFiles: string): number {
    if (maxFiles.endsWith('d')) {
      return parseInt(maxFiles.slice(0, -1));
    } else if (maxFiles.endsWith('w')) {
      return parseInt(maxFiles.slice(0, -1)) * 7;
    } else if (maxFiles.endsWith('m')) {
      return parseInt(maxFiles.slice(0, -1)) * 30;
    } else {
      return parseInt(maxFiles) || 7;
    }
  }

  /**
   * 解析文件大小配置
   * 
   * @param size 大小字符串（如 "10m", "1g", "500k"）
   * @returns 字节数
   * @private
   */
  private parseSize(size: string): number {
    const units: Record<string, number> = {
      'k': 1024,
      'm': 1024 * 1024,
      'g': 1024 * 1024 * 1024,
    };

    const unit = size.slice(-1).toLowerCase();
    const value = parseInt(size.slice(0, -1));

    return value * (units[unit] || 1);
  }

  /**
   * 格式化字节数为可读字符串
   * 
   * @param bytes 字节数
   * @returns 格式化后的字符串
   * @private
   */
  private formatBytes(bytes: number): string {
    if (bytes === 0) return '0 B';

    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
}