/**
 * 数据备份服务
 * @file backupService.js
 * @description 提供数据库备份、文件备份和故障恢复功能
 */
const fs = require('fs').promises;
const path = require('path');
const { spawn } = require('child_process');
const archiver = require('archiver');
const { sequelize } = require('../config/database-simple');
const logger = require('../utils/logger');
const moment = require('moment');

/**
 * 备份配置
 */
const BACKUP_CONFIG = {
  // 备份目录
  backupDir: path.join(__dirname, '../../backups'),
  
  // 数据库备份配置
  database: {
    host: process.env.DB_HOST || 'localhost',
    port: process.env.DB_PORT || 3306,
    database: process.env.DB_NAME || 'nxxm_farming',
    username: process.env.DB_USER || 'root',
    password: process.env.DB_PASS || ''
  },
  
  // 备份保留策略
  retention: {
    daily: 7,     // 保留7天的日备份
    weekly: 4,    // 保留4周的周备份
    monthly: 12   // 保留12个月的月备份
  },
  
  // 压缩级别 (0-9)
  compressionLevel: 6
};

/**
 * 备份服务类
 */
class BackupService {
  constructor() {
    this.isBackupRunning = false;
    this.backupQueue = [];
    this.initBackupDirectory();
  }

  /**
   * 初始化备份目录
   */
  async initBackupDirectory() {
    try {
      await fs.mkdir(BACKUP_CONFIG.backupDir, { recursive: true });
      await fs.mkdir(path.join(BACKUP_CONFIG.backupDir, 'database'), { recursive: true });
      await fs.mkdir(path.join(BACKUP_CONFIG.backupDir, 'files'), { recursive: true });
      await fs.mkdir(path.join(BACKUP_CONFIG.backupDir, 'logs'), { recursive: true });
      
      logger.info('备份目录初始化完成');
    } catch (error) {
      logger.error('备份目录初始化失败:', error);
      throw error;
    }
  }

  /**
   * 执行完整系统备份
   * @param {Object} options 备份选项
   * @returns {Object} 备份结果
   */
  async createFullBackup(options = {}) {
    if (this.isBackupRunning) {
      throw new Error('备份正在进行中，请稍后再试');
    }

    this.isBackupRunning = true;
    const backupId = this.generateBackupId();
    const backupPath = path.join(BACKUP_CONFIG.backupDir, backupId);

    try {
      logger.info(`开始创建完整备份: ${backupId}`);
      
      await fs.mkdir(backupPath, { recursive: true });

      const backupResult = {
        id: backupId,
        timestamp: new Date(),
        type: 'full',
        status: 'in_progress',
        components: {
          database: { status: 'pending', size: 0, path: '' },
          files: { status: 'pending', size: 0, path: '' },
          logs: { status: 'pending', size: 0, path: '' }
        },
        totalSize: 0,
        duration: 0
      };

      const startTime = Date.now();

      // 1. 备份数据库
      logger.info('开始备份数据库...');
      const dbBackupResult = await this.backupDatabase(backupPath);
      backupResult.components.database = dbBackupResult;

      // 2. 备份文件（上传文件、配置文件等）
      logger.info('开始备份文件...');
      const fileBackupResult = await this.backupFiles(backupPath);
      backupResult.components.files = fileBackupResult;

      // 3. 备份日志
      logger.info('开始备份日志...');
      const logBackupResult = await this.backupLogs(backupPath);
      backupResult.components.logs = logBackupResult;

      // 4. 创建压缩包
      logger.info('开始压缩备份文件...');
      const archivePath = await this.createArchive(backupPath, backupId);

      // 5. 计算总大小和持续时间
      const archiveStats = await fs.stat(archivePath);
      backupResult.totalSize = archiveStats.size;
      backupResult.duration = Date.now() - startTime;
      backupResult.status = 'completed';
      backupResult.archivePath = archivePath;

      // 6. 清理临时目录
      await this.cleanupDirectory(backupPath);

      // 7. 保存备份元数据
      await this.saveBackupMetadata(backupResult);

      logger.info(`完整备份创建完成: ${backupId}, 大小: ${this.formatSize(backupResult.totalSize)}, 用时: ${backupResult.duration}ms`);
      
      return backupResult;
    } catch (error) {
      logger.error(`创建备份失败: ${backupId}`, error);
      
      // 清理失败的备份
      try {
        await this.cleanupDirectory(backupPath);
      } catch (cleanupError) {
        logger.error('清理失败备份目录出错:', cleanupError);
      }
      
      throw error;
    } finally {
      this.isBackupRunning = false;
    }
  }

  /**
   * 备份数据库
   * @param {string} backupPath 备份路径
   * @returns {Object} 备份结果
   */
  async backupDatabase(backupPath) {
    return new Promise((resolve, reject) => {
      const timestamp = moment().format('YYYYMMDD_HHmmss');
      const filename = `database_${timestamp}.sql`;
      const outputPath = path.join(backupPath, filename);

      const mysqldumpArgs = [
        '-h', BACKUP_CONFIG.database.host,
        '-P', BACKUP_CONFIG.database.port,
        '-u', BACKUP_CONFIG.database.username,
        `--password=${BACKUP_CONFIG.database.password}`,
        '--single-transaction',
        '--routines',
        '--triggers',
        '--set-gtid-purged=OFF',
        BACKUP_CONFIG.database.database
      ];

      const mysqldump = spawn('mysqldump', mysqldumpArgs);
      const writeStream = require('fs').createWriteStream(outputPath);

      mysqldump.stdout.pipe(writeStream);

      let errorOutput = '';
      mysqldump.stderr.on('data', (data) => {
        errorOutput += data.toString();
      });

      mysqldump.on('close', async (code) => {
        try {
          if (code === 0) {
            const stats = await fs.stat(outputPath);
            resolve({
              status: 'completed',
              size: stats.size,
              path: filename,
              duration: Date.now() - Date.now()
            });
          } else {
            reject(new Error(`mysqldump失败，退出代码: ${code}, 错误: ${errorOutput}`));
          }
        } catch (error) {
          reject(error);
        }
      });

      mysqldump.on('error', (error) => {
        reject(new Error(`mysqldump执行失败: ${error.message}`));
      });
    });
  }

  /**
   * 备份文件
   * @param {string} backupPath 备份路径
   * @returns {Object} 备份结果
   */
  async backupFiles(backupPath) {
    try {
      const fileBackupPath = path.join(backupPath, 'files');
      await fs.mkdir(fileBackupPath, { recursive: true });

      // 备份上传文件（如果存在）
      const uploadsDir = path.join(__dirname, '../../uploads');
      try {
        await fs.access(uploadsDir);
        await this.copyDirectory(uploadsDir, path.join(fileBackupPath, 'uploads'));
      } catch (error) {
        // 上传目录不存在，跳过
        logger.warn('上传目录不存在，跳过文件备份');
      }

      // 备份配置文件
      const configFiles = [
        path.join(__dirname, '../config'),
        path.join(__dirname, '../package.json'),
        path.join(__dirname, '../.env')
      ];

      for (const configPath of configFiles) {
        try {
          await fs.access(configPath);
          const basename = path.basename(configPath);
          const destPath = path.join(fileBackupPath, basename);
          
          const stats = await fs.stat(configPath);
          if (stats.isDirectory()) {
            await this.copyDirectory(configPath, destPath);
          } else {
            await fs.copyFile(configPath, destPath);
          }
        } catch (error) {
          logger.warn(`配置文件 ${configPath} 备份跳过:`, error.message);
        }
      }

      const backupStats = await this.getDirectorySize(fileBackupPath);
      
      return {
        status: 'completed',
        size: backupStats.size,
        path: 'files',
        fileCount: backupStats.fileCount
      };
    } catch (error) {
      logger.error('文件备份失败:', error);
      return {
        status: 'failed',
        error: error.message,
        size: 0,
        path: ''
      };
    }
  }

  /**
   * 备份日志
   * @param {string} backupPath 备份路径
   * @returns {Object} 备份结果
   */
  async backupLogs(backupPath) {
    try {
      const logBackupPath = path.join(backupPath, 'logs');
      await fs.mkdir(logBackupPath, { recursive: true });

      const logsDir = path.join(__dirname, '../logs');
      
      try {
        await fs.access(logsDir);
        await this.copyDirectory(logsDir, logBackupPath);
      } catch (error) {
        logger.warn('日志目录不存在，跳过日志备份');
        return {
          status: 'skipped',
          size: 0,
          path: '',
          message: '日志目录不存在'
        };
      }

      const backupStats = await this.getDirectorySize(logBackupPath);
      
      return {
        status: 'completed',
        size: backupStats.size,
        path: 'logs',
        fileCount: backupStats.fileCount
      };
    } catch (error) {
      logger.error('日志备份失败:', error);
      return {
        status: 'failed',
        error: error.message,
        size: 0,
        path: ''
      };
    }
  }

  /**
   * 创建压缩包
   * @param {string} sourcePath 源路径
   * @param {string} backupId 备份ID
   * @returns {string} 压缩包路径
   */
  async createArchive(sourcePath, backupId) {
    return new Promise((resolve, reject) => {
      const archivePath = path.join(BACKUP_CONFIG.backupDir, `${backupId}.zip`);
      const output = require('fs').createWriteStream(archivePath);
      const archive = archiver('zip', { zlib: { level: BACKUP_CONFIG.compressionLevel } });

      output.on('close', () => {
        logger.info(`压缩包创建完成: ${archivePath}, 大小: ${archive.pointer()} bytes`);
        resolve(archivePath);
      });

      archive.on('error', (error) => {
        reject(error);
      });

      archive.pipe(output);
      archive.directory(sourcePath, false);
      archive.finalize();
    });
  }

  /**
   * 复制目录
   * @param {string} src 源目录
   * @param {string} dest 目标目录
   */
  async copyDirectory(src, dest) {
    await fs.mkdir(dest, { recursive: true });
    const entries = await fs.readdir(src, { withFileTypes: true });

    for (const entry of entries) {
      const srcPath = path.join(src, entry.name);
      const destPath = path.join(dest, entry.name);

      if (entry.isDirectory()) {
        await this.copyDirectory(srcPath, destPath);
      } else {
        await fs.copyFile(srcPath, destPath);
      }
    }
  }

  /**
   * 获取目录大小
   * @param {string} dirPath 目录路径
   * @returns {Object} 大小统计
   */
  async getDirectorySize(dirPath) {
    let totalSize = 0;
    let fileCount = 0;

    try {
      const entries = await fs.readdir(dirPath, { withFileTypes: true });

      for (const entry of entries) {
        const fullPath = path.join(dirPath, entry.name);
        
        if (entry.isDirectory()) {
          const subStats = await this.getDirectorySize(fullPath);
          totalSize += subStats.size;
          fileCount += subStats.fileCount;
        } else {
          const stats = await fs.stat(fullPath);
          totalSize += stats.size;
          fileCount++;
        }
      }
    } catch (error) {
      logger.warn(`获取目录大小失败: ${dirPath}`, error);
    }

    return { size: totalSize, fileCount };
  }

  /**
   * 清理目录
   * @param {string} dirPath 目录路径
   */
  async cleanupDirectory(dirPath) {
    try {
      await fs.rmdir(dirPath, { recursive: true });
    } catch (error) {
      logger.warn(`清理目录失败: ${dirPath}`, error);
    }
  }

  /**
   * 生成备份ID
   * @returns {string} 备份ID
   */
  generateBackupId() {
    const timestamp = moment().format('YYYYMMDD_HHmmss');
    const random = Math.random().toString(36).substring(2, 8);
    return `backup_${timestamp}_${random}`;
  }

  /**
   * 保存备份元数据
   * @param {Object} backupResult 备份结果
   */
  async saveBackupMetadata(backupResult) {
    try {
      const metadataPath = path.join(BACKUP_CONFIG.backupDir, 'metadata.json');
      
      let metadata = [];
      try {
        const existing = await fs.readFile(metadataPath, 'utf8');
        metadata = JSON.parse(existing);
      } catch (error) {
        // 文件不存在或格式错误，使用空数组
      }

      metadata.unshift(backupResult);
      
      // 只保留最近100条记录
      if (metadata.length > 100) {
        metadata = metadata.slice(0, 100);
      }

      await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
      logger.info('备份元数据已保存');
    } catch (error) {
      logger.error('保存备份元数据失败:', error);
    }
  }

  /**
   * 获取备份列表
   * @returns {Array} 备份列表
   */
  async getBackupList() {
    try {
      const metadataPath = path.join(BACKUP_CONFIG.backupDir, 'metadata.json');
      
      try {
        const data = await fs.readFile(metadataPath, 'utf8');
        return JSON.parse(data);
      } catch (error) {
        return [];
      }
    } catch (error) {
      logger.error('获取备份列表失败:', error);
      return [];
    }
  }

  /**
   * 删除备份
   * @param {string} backupId 备份ID
   * @returns {boolean} 删除结果
   */
  async deleteBackup(backupId) {
    try {
      const archivePath = path.join(BACKUP_CONFIG.backupDir, `${backupId}.zip`);
      
      try {
        await fs.unlink(archivePath);
        logger.info(`备份文件已删除: ${archivePath}`);
      } catch (error) {
        logger.warn(`备份文件删除失败或不存在: ${archivePath}`);
      }

      // 更新元数据
      const metadata = await this.getBackupList();
      const updatedMetadata = metadata.filter(backup => backup.id !== backupId);
      
      const metadataPath = path.join(BACKUP_CONFIG.backupDir, 'metadata.json');
      await fs.writeFile(metadataPath, JSON.stringify(updatedMetadata, null, 2));

      return true;
    } catch (error) {
      logger.error(`删除备份失败: ${backupId}`, error);
      return false;
    }
  }

  /**
   * 自动清理过期备份
   */
  async cleanupExpiredBackups() {
    try {
      const backups = await this.getBackupList();
      const now = moment();
      let deletedCount = 0;

      for (const backup of backups) {
        const backupDate = moment(backup.timestamp);
        const daysDiff = now.diff(backupDate, 'days');

        let shouldDelete = false;

        // 根据备份类型和时间决定是否删除
        if (backup.type === 'daily' && daysDiff > BACKUP_CONFIG.retention.daily) {
          shouldDelete = true;
        } else if (backup.type === 'weekly' && daysDiff > (BACKUP_CONFIG.retention.weekly * 7)) {
          shouldDelete = true;
        } else if (backup.type === 'monthly' && daysDiff > (BACKUP_CONFIG.retention.monthly * 30)) {
          shouldDelete = true;
        } else if (backup.type === 'full' && daysDiff > BACKUP_CONFIG.retention.daily) {
          // 完整备份使用日备份的保留策略
          shouldDelete = true;
        }

        if (shouldDelete) {
          const deleted = await this.deleteBackup(backup.id);
          if (deleted) {
            deletedCount++;
          }
        }
      }

      if (deletedCount > 0) {
        logger.info(`自动清理完成，删除了 ${deletedCount} 个过期备份`);
      }

      return deletedCount;
    } catch (error) {
      logger.error('自动清理过期备份失败:', error);
      return 0;
    }
  }

  /**
   * 恢复数据库
   * @param {string} backupId 备份ID
   * @returns {boolean} 恢复结果
   */
  async restoreDatabase(backupId) {
    try {
      const backups = await this.getBackupList();
      const backup = backups.find(b => b.id === backupId);
      
      if (!backup) {
        throw new Error('备份不存在');
      }

      const archivePath = path.join(BACKUP_CONFIG.backupDir, `${backupId}.zip`);
      
      // 这里可以实现数据库恢复逻辑
      // 由于涉及到解压和执行SQL脚本，需要谨慎实现
      logger.info(`数据库恢复功能需要进一步实现: ${backupId}`);
      
      return true;
    } catch (error) {
      logger.error(`恢复数据库失败: ${backupId}`, error);
      return false;
    }
  }

  /**
   * 获取备份统计
   * @returns {Object} 统计信息
   */
  async getBackupStats() {
    try {
      const backups = await this.getBackupList();
      
      const stats = {
        total: backups.length,
        totalSize: backups.reduce((sum, backup) => sum + (backup.totalSize || 0), 0),
        byType: {
          full: backups.filter(b => b.type === 'full').length,
          daily: backups.filter(b => b.type === 'daily').length,
          weekly: backups.filter(b => b.type === 'weekly').length,
          monthly: backups.filter(b => b.type === 'monthly').length
        },
        byStatus: {
          completed: backups.filter(b => b.status === 'completed').length,
          failed: backups.filter(b => b.status === 'failed').length,
          inProgress: backups.filter(b => b.status === 'in_progress').length
        },
        latest: backups[0] || null,
        oldestRetained: backups[backups.length - 1] || null
      };

      return stats;
    } catch (error) {
      logger.error('获取备份统计失败:', error);
      return {
        total: 0,
        totalSize: 0,
        byType: {},
        byStatus: {},
        latest: null,
        oldestRetained: null
      };
    }
  }

  /**
   * 格式化文件大小
   * @param {number} bytes 字节数
   * @returns {string} 格式化的大小
   */
  formatSize(bytes) {
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
    if (bytes === 0) return '0 B';
    const i = Math.floor(Math.log(bytes) / Math.log(1024));
    return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
  }

  /**
   * 检查备份系统健康状态
   * @returns {Object} 健康状态
   */
  async checkBackupHealth() {
    try {
      const stats = await this.getBackupStats();
      const diskUsage = await this.getDiskUsage();
      
      const health = {
        status: 'healthy',
        issues: [],
        stats,
        diskUsage,
        lastBackup: stats.latest,
        recommendations: []
      };

      // 检查磁盘空间
      if (diskUsage.usagePercent > 90) {
        health.status = 'warning';
        health.issues.push('磁盘空间不足（使用率超过90%）');
        health.recommendations.push('清理过期备份或扩展存储空间');
      }

      // 检查最近备份时间
      if (stats.latest) {
        const daysSinceLastBackup = moment().diff(moment(stats.latest.timestamp), 'days');
        if (daysSinceLastBackup > 1) {
          health.status = 'warning';
          health.issues.push(`最近备份时间过久（${daysSinceLastBackup}天前）`);
          health.recommendations.push('建议执行新的备份');
        }
      } else {
        health.status = 'error';
        health.issues.push('没有找到任何备份记录');
        health.recommendations.push('立即创建第一个备份');
      }

      return health;
    } catch (error) {
      logger.error('检查备份健康状态失败:', error);
      return {
        status: 'error',
        issues: ['无法检查备份状态'],
        error: error.message
      };
    }
  }

  /**
   * 获取磁盘使用情况
   * @returns {Object} 磁盘使用情况
   */
  async getDiskUsage() {
    try {
      const stats = await fs.stat(BACKUP_CONFIG.backupDir);
      // 简化的磁盘使用情况检查
      // 在实际环境中可以使用更精确的磁盘空间检查
      return {
        total: 100 * 1024 * 1024 * 1024, // 假设100GB
        used: stats.size || 0,
        free: 100 * 1024 * 1024 * 1024 - (stats.size || 0),
        usagePercent: ((stats.size || 0) / (100 * 1024 * 1024 * 1024)) * 100
      };
    } catch (error) {
      logger.error('获取磁盘使用情况失败:', error);
      return {
        total: 0,
        used: 0,
        free: 0,
        usagePercent: 0
      };
    }
  }
}

// 创建单例实例
const backupService = new BackupService();

module.exports = backupService;
