/**
 * 存储使用情况缓存管理器
 * ===== P0 性能优化：解决 OSS 统计性能瓶颈 =====
 *
 * 问题：每次获取存储使用情况都要遍历所有 OSS 对象，极其耗时
 * 解决方案：使用数据库字段 storage_used 维护缓存，上传/删除时更新
 *
 * @module StorageUsageCache
 */

const { UserDB } = require('../database');

/**
 * 存储使用情况缓存类
 */
class StorageUsageCache {
  /**
   * 获取用户的存储使用情况（从缓存）
   * @param {number} userId - 用户ID
   * @returns {Promise<{totalSize: number, totalSizeFormatted: string, fileCount: number, cached: boolean}>}
   */
  static async getUsage(userId) {
    try {
      const user = UserDB.findById(userId);

      if (!user) {
        throw new Error('用户不存在');
      }

      // 从数据库缓存读取
      const storageUsed = user.storage_used || 0;

      // 导入格式化函数
      const { formatFileSize } = require('../storage');

      return {
        totalSize: storageUsed,
        totalSizeFormatted: formatFileSize(storageUsed),
        fileCount: null, // 缓存模式不统计文件数
        cached: true
      };
    } catch (error) {
      console.error('[存储缓存] 获取失败:', error);
      throw error;
    }
  }

  /**
   * 更新用户的存储使用量
   * @param {number} userId - 用户ID
   * @param {number} deltaSize - 变化量（正数为增加，负数为减少）
   * @returns {Promise<boolean>}
   */
  static async updateUsage(userId, deltaSize) {
    try {
      const numericDelta = Number(deltaSize);
      if (!Number.isFinite(numericDelta)) {
        throw new Error('deltaSize 必须是有效数字');
      }

      // 直接执行 SQL 原子更新，并保证不小于 0
      const { db } = require('../database');
      db.prepare(`
        UPDATE users
        SET storage_used = MAX(COALESCE(storage_used, 0) + ?, 0)
        WHERE id = ?
      `).run(numericDelta, userId);

      console.log(`[存储缓存] 用户 ${userId} 存储变化: ${numericDelta > 0 ? '+' : ''}${numericDelta} 字节`);
      return true;
    } catch (error) {
      console.error('[存储缓存] 更新失败:', error);
      throw error;
    }
  }

  /**
   * 重置用户的存储使用量（管理员功能，用于全量统计后更新缓存）
   * @param {number} userId - 用户ID
   * @param {number} totalSize - 实际总大小
   * @returns {Promise<boolean>}
   */
  static async resetUsage(userId, totalSize) {
    try {
      // 使用直接SQL更新，绕过UserDB.update()的字段白名单限制
      const { db } = require('../database');
      db.prepare(`
        UPDATE users
        SET storage_used = ?
        WHERE id = ?
      `).run(totalSize, userId);

      console.log(`[存储缓存] 用户 ${userId} 存储重置: ${totalSize} 字节`);
      return true;
    } catch (error) {
      console.error('[存储缓存] 重置失败:', error);
      throw error;
    }
  }

  /**
   * 验证并修复缓存（管理员功能）
   * 通过全量统计对比缓存值，如果不一致则更新
   * @param {number} userId - 用户ID
   * @param {OssStorageClient} ossClient - OSS 客户端实例
   * @returns {Promise<{actual: number, cached: number, corrected: boolean}>}
   */
  static async validateAndFix(userId, ossClient) {
    try {
      const { ListObjectsV2Command } = require('@aws-sdk/client-s3');
      const user = UserDB.findById(userId);

      if (!user) {
        throw new Error('用户不存在');
      }

      // 执行全量统计
      let totalSize = 0;
      let continuationToken = null;

      do {
        const command = new ListObjectsV2Command({
          Bucket: ossClient.getBucket(), // 使用ossClient的getBucket()方法以支持系统级统一OSS配置
          Prefix: `user_${userId}/`,
          ContinuationToken: continuationToken
        });

        const response = await ossClient.s3Client.send(command);

        if (response.Contents) {
          for (const obj of response.Contents) {
            totalSize += obj.Size || 0;
          }
        }

        continuationToken = response.NextContinuationToken;
      } while (continuationToken);

      const cached = user.storage_used || 0;
      const corrected = totalSize !== cached;

      if (corrected) {
        await this.resetUsage(userId, totalSize);
        console.log(`[存储缓存] 用户 ${userId} 缓存已修复: ${cached} → ${totalSize}`);
      }

      return {
        actual: totalSize,
        cached,
        corrected
      };
    } catch (error) {
      console.error('[存储缓存] 验证修复失败:', error);
      throw error;
    }
  }

  /**
   * 检查缓存完整性（第二轮修复：缓存一致性保障）
   * 对比缓存值与实际 OSS 存储使用情况，但不自动修复
   * @param {number} userId - 用户ID
   * @param {OssStorageClient} ossClient - OSS 客户端实例
   * @returns {Promise<{consistent: boolean, cached: number, actual: number, diff: number}>}
   */
  static async checkIntegrity(userId, ossClient) {
    try {
      const { ListObjectsV2Command } = require('@aws-sdk/client-s3');
      const user = UserDB.findById(userId);

      if (!user) {
        throw new Error('用户不存在');
      }

      // 执行全量统计
      let totalSize = 0;
      let fileCount = 0;
      let continuationToken = null;

      do {
        const command = new ListObjectsV2Command({
          Bucket: ossClient.getBucket(), // 使用ossClient的getBucket()方法以支持系统级统一OSS配置
          Prefix: `user_${userId}/`,
          ContinuationToken: continuationToken
        });

        const response = await ossClient.s3Client.send(command);

        if (response.Contents) {
          for (const obj of response.Contents) {
            totalSize += obj.Size || 0;
            fileCount++;
          }
        }

        continuationToken = response.NextContinuationToken;
      } while (continuationToken);

      const cached = user.storage_used || 0;
      const diff = totalSize - cached;
      const consistent = Math.abs(diff) === 0;

      console.log(`[存储缓存] 用户 ${userId} 完整性检查: 缓存=${cached}, 实际=${totalSize}, 差异=${diff}`);

      return {
        consistent,
        cached,
        actual: totalSize,
        fileCount,
        diff
      };
    } catch (error) {
      console.error('[存储缓存] 完整性检查失败:', error);
      throw error;
    }
  }

  /**
   * 重建缓存（第二轮修复：缓存一致性保障）
   * 强制从 OSS 全量统计并更新缓存值，绕过一致性检查
   * @param {number} userId - 用户ID
   * @param {OssStorageClient} ossClient - OSS 客户端实例
   * @returns {Promise<{previous: number, current: number, fileCount: number}>}
   */
  static async rebuildCache(userId, ossClient) {
    try {
      const { ListObjectsV2Command } = require('@aws-sdk/client-s3');
      const user = UserDB.findById(userId);

      if (!user) {
        throw new Error('用户不存在');
      }

      console.log(`[存储缓存] 开始重建用户 ${userId} 的缓存...`);

      // 执行全量统计
      let totalSize = 0;
      let fileCount = 0;
      let continuationToken = null;

      do {
        const command = new ListObjectsV2Command({
          Bucket: ossClient.getBucket(), // 使用ossClient的getBucket()方法以支持系统级统一OSS配置
          Prefix: `user_${userId}/`,
          ContinuationToken: continuationToken
        });

        const response = await ossClient.s3Client.send(command);

        if (response.Contents) {
          for (const obj of response.Contents) {
            totalSize += obj.Size || 0;
            fileCount++;
          }
        }

        continuationToken = response.NextContinuationToken;
      } while (continuationToken);

      const previous = user.storage_used || 0;

      // 强制更新缓存
      await this.resetUsage(userId, totalSize);

      console.log(`[存储缓存] 用户 ${userId} 缓存重建完成: ${previous} → ${totalSize} (${fileCount} 个文件)`);

      return {
        previous,
        current: totalSize,
        fileCount
      };
    } catch (error) {
      console.error('[存储缓存] 重建缓存失败:', error);
      throw error;
    }
  }

  /**
   * 检查所有用户的缓存一致性（第二轮修复：批量检查）
   * @param {Array} users - 用户列表
   * @param {Function} getOssClient - 获取 OSS 客户端的函数
   * @returns {Promise<Array>} 检查结果列表
   */
  static async checkAllUsersIntegrity(users, getOssClient) {
    const results = [];

    for (const user of users) {
      // 跳过没有配置 OSS 的用户（需要检查系统级统一配置）
      const { SettingsDB } = require('../database');
      const hasUnifiedConfig = SettingsDB.hasUnifiedOssConfig();
      if (!user.has_oss_config && !hasUnifiedConfig) {
        continue;
      }

      try {
        const ossClient = getOssClient(user);
        const checkResult = await this.checkIntegrity(user.id, ossClient);

        results.push({
          userId: user.id,
          username: user.username,
          ...checkResult
        });
      } catch (error) {
        console.error(`[存储缓存] 检查用户 ${user.id} 失败:`, error.message);
        results.push({
          userId: user.id,
          username: user.username,
          error: error.message
        });
      }
    }

    return results;
  }

  /**
   * 自动检测并修复缓存不一致（第二轮修复：自动化保障）
   * 当检测到不一致时自动修复，并记录日志
   * @param {number} userId - 用户ID
   * @param {OssStorageClient} ossClient - OSS 客户端实例
   * @param {number} threshold - 差异阈值（字节），默认 0（任何差异都修复）
   * @returns {Promise<{autoFixed: boolean, diff: number}>}
   */
  static async autoDetectAndFix(userId, ossClient, threshold = 0) {
    try {
      const checkResult = await this.checkIntegrity(userId, ossClient);

      if (!checkResult.consistent && Math.abs(checkResult.diff) > threshold) {
        console.warn(`[存储缓存] 检测到用户 ${userId} 缓存不一致: 差异 ${checkResult.diff} 字节`);

        // 自动修复
        await this.rebuildCache(userId, ossClient);

        return {
          autoFixed: true,
          diff: checkResult.diff
        };
      }

      return {
        autoFixed: false,
        diff: checkResult.diff
      };
    } catch (error) {
      console.error('[存储缓存] 自动检测修复失败:', error);
      throw error;
    }
  }
}

module.exports = StorageUsageCache;
