/**
 * 腾讯云COS管理器 - 完整版
 * 支持MD5去重、文件记录保存、头像上传和动态图片上传两种场景
 * 参考文档: https://cloud.tencent.com/document/product/436/31953
 */

const COS = require('../dist/cos-wx-sdk-v5.min.js');
const config = require('../config/config.js');
const apiClient = require('./api-client.js');
const errorHandler = require('./error-handler.js');
const SparkMD5 = require('../dist/spark-md5.min.js');

class COSManager {
  constructor() {
    this.cosInstance = null;
    this.credentials = null; // 缓存的临时密钥（兼容旧代码）
    this.credentialsExpireTime = 0; // 密钥过期时间（兼容旧代码）
    this.credentialsCache = {}; // 按 fileType 缓存的临时密钥 { fileType: { credentials, expireTime } }
    this.currentFileType = null; // 当前正在上传的文件类型
    this.isInitialized = false;
  }

  /**
   * 初始化COS实例
   */
  init() {
    if (this.isInitialized && this.cosInstance) {
      return;
    }

    try {
      this.cosInstance = new COS({
        // 强烈推荐: 高级上传内部对小文件使用putObject
        SimpleUploadMethod: 'putObject',
        
        getAuthorization: (options, callback) => {
          // 使用当前正在上传的文件类型，而不是从 options.Scope 获取
          // 因为 options.Scope?.[0]?.action 可能不是我们期望的 fileType
          const fileType = this.currentFileType || 'image';
          
          // 从后端获取临时密钥
          this.getCredentials(fileType)
            .then(credentials => {
              callback({
                TmpSecretId: credentials.tmpSecretId,
                TmpSecretKey: credentials.tmpSecretKey,
                SecurityToken: credentials.sessionToken,
                StartTime: credentials.startTime,
                ExpiredTime: credentials.expiredTime
              });
            })
            .catch(error => {
              console.error('[COS] 获取临时密钥失败:', error);
              callback({
                TmpSecretId: '',
                TmpSecretKey: '',
                SecurityToken: '',
                StartTime: 0,
                ExpiredTime: 0
              });
            });
        }
      });

      this.isInitialized = true;
      console.log('[COS] 初始化成功');
    } catch (error) {
      console.error('[COS] 初始化失败:', error);
      errorHandler.handleError(error, {
        action: 'cos_init',
        showToast: true
      });
      throw error;
    }
  }

  /**
   * 获取临时密钥
   * @param {string} fileType - 文件类型 (image/video/audio/file/avatar)
   * @returns {Promise<Object>} 临时密钥信息
   */
  async getCredentials(fileType = 'image') {
    try {
      // 检查按 fileType 缓存的密钥是否还有效(提前5分钟过期)
      const now = Math.floor(Date.now() / 1000);
      const cached = this.credentialsCache[fileType];
      
      if (cached && cached.credentials && cached.expireTime > now + 300) {
        // 验证缓存的密钥是否完整
        const creds = cached.credentials;
        if (creds.tmpSecretId && creds.tmpSecretKey && creds.sessionToken) {
          console.log(`[COS] 使用缓存的 ${fileType} 临时密钥`);
          return creds;
        } else {
          // 缓存的密钥不完整，清除缓存
          console.warn(`[COS] 缓存的 ${fileType} 临时密钥不完整，重新获取`);
          delete this.credentialsCache[fileType];
        }
      }

      // 从后端获取新的临时密钥
      console.log(`[COS] 获取新的 ${fileType} 临时密钥`);
      const response = await apiClient.get(config.cos.stsUrl, { fileType });

      if (response.code === 200 && response.data) {
        const credentials = response.data;
        
        // 验证返回的密钥是否完整
        if (!credentials.tmpSecretId || !credentials.tmpSecretKey || !credentials.sessionToken) {
          throw new Error('临时密钥数据不完整');
        }
        
        // 按 fileType 缓存密钥
        this.credentialsCache[fileType] = {
          credentials: credentials,
          expireTime: credentials.expiredTime
        };
        
        // 兼容旧代码：也更新全局 credentials
        this.credentials = credentials;
        this.credentialsExpireTime = credentials.expiredTime;
        
        return credentials;
      }

      throw new Error(response.message || '获取临时密钥失败');
    } catch (error) {
      console.error('[COS] 获取临时密钥失败:', error);
      // 清除可能损坏的缓存
      if (this.credentialsCache[fileType]) {
        delete this.credentialsCache[fileType];
      }
      throw error;
    }
  }

  /**
   * 计算文件MD5
   * @param {string} filePath - 微信临时文件路径
   * @returns {Promise<string>} MD5值
   */
  async calculateMD5(filePath) {
    return new Promise((resolve, reject) => {
      try {
        const fileSystemManager = wx.getFileSystemManager();

        // 读取文件内容
        fileSystemManager.readFile({
          filePath,
          success: (res) => {
            try {
              // 使用SparkMD5计算MD5
              const spark = new SparkMD5.ArrayBuffer();
              spark.append(res.data);
              const md5 = spark.end();
              resolve(md5);
            } catch (error) {
              console.error('[COS] MD5计算失败:', error);
              reject(error);
            }
          },
          fail: (error) => {
            console.error('[COS] 读取文件失败:', error);
            reject(error);
          }
        });
      } catch (error) {
        console.error('[COS] 计算MD5异常:', error);
        reject(error);
      }
    });
  }

  /**
   * 检查文件是否已存在(用于去重)
   * @param {string} md5Hash - 文件MD5值
   * @returns {Promise<Object>} 检查结果 {exists, fileUrl, fileId, fileSize}
   */
  async checkFileExists(md5Hash) {
    try {
      const response = await apiClient.post(config.cos.fileCheckUrl, { md5Hash });

      if (response.code === 200 && response.data) {
        return response.data;
      }

      return { exists: false };
    } catch (error) {
      console.error('[COS] 检查文件存在性失败:', error);
      // 检查失败不影响上传流程,返回不存在
      return { exists: false };
    }
  }

  /**
   * 保存文件记录到后端
   * @param {Object} fileInfo - 文件信息
   * @returns {Promise<Object>} 保存结果
   */
  async saveFileRecord(fileInfo) {
    try {
      const response = await apiClient.post(config.cos.fileRecordUrl, fileInfo);

      if (response.code === 200) {
        return response.data;
      }

      throw new Error(response.message || '保存文件记录失败');
    } catch (error) {
      console.error('[COS] 保存文件记录失败:', error);
      throw error;
    }
  }

  /**
   * 生成文件路径
   * @param {string} fileName - 原始文件名
   * @param {string} fileType - 文件类型
   * @param {number} userId - 用户ID
   * @returns {string} 完整文件路径
   */
  generateFilePath(fileName, fileType, userId) {
    const timestamp = Math.floor(Date.now() / 1000);
    // 路径格式: user/{userId}/{fileType}/{timestamp}_{fileName}
    return `user/${userId}/${fileType}/${timestamp}_${fileName}`;
  }

  /**
   * 生成文件URL
   * @param {string} objectPath - 对象路径
   * @returns {string} 完整URL
   */
  generateFileUrl(objectPath) {
    const { bucket, region } = config.cos;
    return `https://${bucket}.cos.${region}.myqcloud.com/${objectPath}`;
  }

  /**
   * 生成极致压缩的图片URL - 腾讯云COS图片处理
   * 确保压缩到10KB以内
   * @param {string} objectPath - 对象路径
   * @param {Object} options - 压缩选项
   * @returns {string} 压缩后的图片URL
   */
  generateUltraCompressedImageUrl(objectPath, options = {}) {
    const { bucket, region } = config.cos;
    return `https://${bucket}.cos.${region}.myqcloud.com/${objectPath}`;
  }

  /**
   * 本地预压缩到10KB以内 - 使用微信canvas
   * @param {string} filePath - 原始图片路径
   * @param {Object} options - 压缩选项
   * @returns {Promise<string>} 压缩后的临时文件路径
   */
  async compressToUnder10KB(filePath, options = {}) {
    return new Promise((resolve, reject) => {
      const {
        maxWidth = 400,
        maxHeight = 400,
        maxAttempts = 3,      // 最大尝试次数
        targetSize = 500 * 1024 // 目标大小：10KB
      } = options;


      console.log('[DEBUG] 解构后的targetSize:', targetSize);

      let attempt = 0;
      let currentQuality = 0.9; // 起始质量30%

      const tryCompress = () => {
        attempt++;
        if (attempt > maxAttempts) {
          reject(new Error(`无法压缩到10KB以内，最小体积: ${lastSize}字节`));
          return;
        }

        wx.getImageInfo({
          src: filePath,
          success: (imageInfo) => {
            const { width: originalWidth, height: originalHeight } = imageInfo;
            
            // 计算压缩尺寸
            let targetWidth = originalWidth;
            let targetHeight = originalHeight;
            
            if (originalWidth > maxWidth || originalHeight > maxHeight) {
              const ratio = Math.min(maxWidth / originalWidth, maxHeight / originalHeight);
              targetWidth = Math.floor(originalWidth * ratio);
              targetHeight = Math.floor(originalHeight * ratio);
            }

            // 创建离屏canvas
            const canvas = wx.createOffscreenCanvas({
              type: '2d',
              width: targetWidth,
              height: targetHeight
            });

            const ctx = canvas.getContext('2d');
            
            // 创建图片对象
            const image = canvas.createImage();
            image.onload = () => {
              // 绘制到canvas
              ctx.clearRect(0, 0, targetWidth, targetHeight);
              ctx.drawImage(image, 0, 0, targetWidth, targetHeight);

              // 转换为临时文件
              wx.canvasToTempFilePath({
                canvas: canvas,
                quality: currentQuality,
                fileType: 'jpg', // 使用jpg格式，压缩效果更好
                success: async (res) => {
                  try {
                    // 获取文件大小
                    const fileInfo = await this.getFileInfo(res.tempFilePath);
                    
                    console.log(`[COS] 压缩尝试 ${attempt}: 质量${currentQuality}, 输入的targetSize:${targetSize},尺寸${targetWidth}x${targetHeight}, 大小${fileInfo.size}字节`);
                    
                    if (fileInfo.size <= targetSize) {
                      // 达到目标大小
                      resolve(res.tempFilePath);
                    } else {
                      // 继续压缩：降低质量
                      currentQuality = Math.max(0.1, currentQuality * 0.9); // 质量降低40%
                      tryCompress();
                    }
                  } catch (error) {
                    reject(error);
                  }
                },
                fail: (err) => {
                  reject(new Error(`canvas转换失败: ${err.errMsg}`));
                }
              }, this);
            };

            image.onerror = () => {
              reject(new Error('图片加载失败'));
            };

            image.src = filePath;
          },
          fail: (err) => {
            reject(new Error(`获取图片信息失败: ${err.errMsg}`));
          }
        });
      };

      tryCompress();
    });
  }

  /**
   * 核心上传方法 - 统一接口
   * @param {string|string[]} filePath - 文件路径(单个或数组)
   * @param {Object} options - 上传配置
   * @param {string} options.fileType - 文件类型: 'avatar'|'image'|'video'|'audio' (默认'image')
   * @param {boolean} options.enableDedup - 是否去重 (默认false)
   * @param {boolean} options.enableCompress - 是否启用极致压缩到10KB以内 (默认false)
   * @param {Object} options.compressOptions - 压缩配置
   * @param {Function} options.onProgress - 进度回调
   * @returns {Promise<Object|Array>} 上传结果(单个对象或数组)
   */
  async upload(filePath, { 
    fileType = 'image', 
    enableDedup = false, 
    enableCompress = false,
    compressOptions = {},
    onProgress 
  } = {}) {
    // 批量上传
    if (Array.isArray(filePath)) {
      return this._uploadMultiple(filePath, { 
        fileType, 
        enableDedup, 
        enableCompress,
        compressOptions, 
        onProgress 
      });
    }

    // 单文件上传
    return this._uploadSingle(filePath, { 
      fileType, 
      enableDedup, 
      enableCompress,
      compressOptions, 
      onProgress 
    });
  }

  /**
   * 单文件上传(内部方法)
   */
  async _uploadSingle(filePath, { fileType, enableDedup, enableCompress, compressOptions, onProgress }) {
    try {
      if (!this.isInitialized) this.init();

      const userId = this._getUserId();
      let finalFilePath = filePath;
      let isCompressed = false;
      console.log("cos-manager--->","enableCompress1: "+enableCompress+" compressOptions "+compressOptions+" fileUrl: "+fileUrl);

      // 极致压缩处理（仅对图片）
      if (enableCompress && this._isImageFile(filePath)) {
        try {
          finalFilePath = await this.compressToUnder10KB(filePath, compressOptions);
          isCompressed = true;
          console.log('[COS] 图片极致压缩完成');
        } catch (compressError) {
          console.warn('[COS] 图片极致压缩失败,使用原文件:', compressError);
          // 压缩失败时使用原文件
          finalFilePath = filePath;
        }
      }

      const fileName = finalFilePath.split('/').pop();
      const fileInfo = await this.getFileInfo(finalFilePath);

      // 去重检查
      if (enableDedup) {
        const dedupResult = await this._handleDedup(finalFilePath);
        if (dedupResult) {
          // 如果压缩过，清理临时文件
          if (isCompressed && finalFilePath !== filePath) {
            this._cleanupTempFile(finalFilePath);
          }
          return {
            ...dedupResult,
            isCompressed: false // 去重文件不进行压缩
          };
        }
      }

      // 上传流程
      const { objectPath, fileUrl, md5Hash } = await this._executeUpload(
        finalFilePath, fileName, fileType, userId, onProgress
      );

      // 生成极致压缩URL（如果启用压缩）
      let  compressedUrl = enableCompress && isCompressed ? 
        this.generateUltraCompressedImageUrl(objectPath, compressOptions) : fileUrl;

      // 确定usageType(用于后端自动处理)
      const usageType = fileType === 'avatar' ? 'avatar' : 'feed';
       console.log("cos-manager--->","enableCompress: "+enableCompress+" compressedUrl "+compressedUrl+" fileUrl: "+fileUrl);
      // 保存记录
      const recordResult = await this._saveRecord({
        fileName, 
        fileInfo, 
        fileType, 
        objectPath, 
        fileUrl: compressedUrl,
        originalUrl: fileUrl, // 保存原始URL
        md5Hash, 
        usageType, 
        relatedId: 0
      });

      // 清理临时压缩文件
      if (isCompressed && finalFilePath !== filePath) {
        this._cleanupTempFile(finalFilePath);
      }

      const finalFileInfo = await this.getFileInfo(finalFilePath);
      
      return {
        success: true,
        fileUrl: recordResult.fileUrl,
        originalUrl: fileUrl,
        fileId: recordResult.fileId,
        fileSize: finalFileInfo.size,
        md5Hash,
        isDeduped: false,
        isCompressed: enableCompress && isCompressed,
        message: `文件上传成功${enableCompress && isCompressed ? '(已极致压缩到10KB以内)' : ''}`
      };
    } catch (error) {
      console.error('[COS] 上传失败:', error);
      errorHandler.handleError(error, {
        action: 'cos_upload',
        showToast: true,
        toastMessage: '文件上传失败,请重试'
      });
      throw error;
    }
  }

  /**
   * 批量上传(内部方法)
   */
  async _uploadMultiple(filePaths, { fileType, enableDedup, enableCompress, compressOptions, onProgress }) {
    const results = [];
    const total = filePaths.length;

    for (let i = 0; i < total; i++) {
      try {
        const result = await this._uploadSingle(filePaths[i], {
          fileType,
          enableDedup,
          enableCompress,
          compressOptions,
          onProgress: (percent) => {
            if (onProgress) {
              onProgress({
                current: i + 1,
                total,
                currentPercent: percent,
                overallPercent: Math.round(((i + percent / 100) / total) * 100)
              });
            }
          }
        });
        results.push(result);
      } catch (error) {
        console.error(`[COS] 文件 ${i + 1} 上传失败:`, error);
        results.push({
          success: false,
          error: error.message || '上传失败'
        });
      }
    }

    return results;
  }

  // ==================== 内部辅助方法 ====================

  /**
   * 获取用户ID
   */
  _getUserId() {
    const userInfo = wx.getStorageSync('userInfo');
    const customId = userInfo.user?.customId;
    if (!userInfo || !customId) {
      throw new Error('用户未登录');
    }
    return customId;
  }

  /**
   * 处理去重逻辑
   */
  async _handleDedup(filePath) {
    const md5Hash = await this.calculateMD5(filePath);
    const checkResult = await this.checkFileExists(md5Hash);

    if (checkResult.exists) {
      return {
        success: true,
        fileUrl: checkResult.fileUrl,
        originalUrl: checkResult.fileUrl,
        fileId: checkResult.fileId,
        fileSize: checkResult.fileSize,
        md5Hash,
        isDeduped: true,
        isCompressed: false,
        message: '文件已存在,直接使用'
      };
    }
    return null;
  }

  /**
   * 执行上传到COS
   */
  async _executeUpload(filePath, fileName, fileType, userId, onProgress) {
    // 设置当前正在上传的文件类型，供 getAuthorization 回调使用
    this.currentFileType = fileType;
    
    try {
      // 先获取对应 fileType 的临时密钥（确保在上传前获取）
      const credentials = await this.getCredentials(fileType);
      
      // 验证临时密钥是否有效
      if (!credentials || !credentials.tmpSecretId || !credentials.tmpSecretKey || !credentials.sessionToken) {
        throw new Error('临时密钥无效，请重新获取');
      }

      const objectPath = this.generateFilePath(fileName, fileType, userId);
      const fileUrl = this.generateFileUrl(objectPath);

      await this.uploadToCOS({ filePath, key: objectPath, onProgress });
      const md5Hash = await this.calculateMD5(filePath);

      return { objectPath, fileUrl, md5Hash };
    } catch (error) {
      console.error('[COS] 上传执行失败:', error);
      // 清除缓存的临时密钥，强制下次重新获取
      if (this.credentialsCache[fileType]) {
        delete this.credentialsCache[fileType];
      }
      throw error;
    } finally {
      // 上传完成后清除当前文件类型
      this.currentFileType = null;
    }
  }

  /**
   * 保存文件记录
   */
  async _saveRecord({ fileName, fileInfo, fileType, objectPath, fileUrl, md5Hash, usageType, relatedId }) {
    return await this.saveFileRecord({
      fileName,
      fileSize: fileInfo.size,
      contentType: fileInfo.type || this.getContentType(fileName),
      fileType,
      objectPath,
      fileUrl,
      md5Hash,
      usageType,
      relatedId
    });
  }

  /**
   * 检查是否为图片文件
   */
  _isImageFile(filePath) {
    const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
    const ext = filePath.split('.').pop().toLowerCase();
    return imageExts.includes(ext);
  }

  /**
   * 清理临时文件
   */
  _cleanupTempFile(filePath) {
    try {
      if (filePath && filePath.startsWith('http://tmp/')) {
        wx.getFileSystemManager().unlink({
          filePath: filePath,
          fail: (err) => {
            console.warn('[COS] 临时文件清理失败:', err);
          }
        });
      }
    } catch (error) {
      console.warn('[COS] 临时文件清理异常:', error);
    }
  }

  /**
   * 直接上传到COS(不经过去重和记录保存)
   */
  uploadToCOS({ filePath, key, onProgress }) {
    return new Promise((resolve, reject) => {
      this.cosInstance.uploadFile({
        Bucket: config.cos.bucket,
        Region: config.cos.region,
        Key: key,
        FilePath: filePath,
        SliceSize: 1024 * 1024 * 5,
        onProgress: (progressData) => {
          if (onProgress) {
            const percent = Math.round(progressData.percent * 100);
            onProgress(percent);
          }
        }
      }, (err, data) => {
        if (err) {
          console.error('[COS] 上传失败:', err.code, err.message);
          if (err.statusCode === 403 || err.code === 'AccessDenied') {
            console.error('[COS] 权限错误,请检查临时密钥配置');
          }
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }

  /**
   * 批量上传文件
   * @deprecated 请使用 upload(filePaths, options) 代替
   */
  async uploadFiles(files, options = {}) {
    console.warn('[COS] uploadFiles已废弃,请使用upload()方法');
    const filePaths = files.map(f => f.filePath || f);
    return this.upload(filePaths, options);
  }

  /**
   * 完整上传方法(保留向后兼容)
   * @deprecated 请使用 upload(filePath, options) 代替
   */
  async uploadFile(options) {
    console.warn('[COS] uploadFile已废弃,请使用upload()方法');
    const { filePath, fileType = 'image', enableDedup = false, onProgress } = options;
    return this.upload(filePath, { fileType, enableDedup, onProgress });
  }

  /**
   * 获取文件信息
   * @param {string} filePath - 文件路径
   * @returns {Promise<Object>} 文件信息
   */
  getFileInfo(filePath) {
    return new Promise((resolve, reject) => {
      wx.getFileInfo({
        filePath,
        success: (res) => {
          const ext = filePath.split('.').pop().toLowerCase();
          const type = this.getContentType('file.' + ext);

          resolve({
            size: res.size,
            type
          });
        },
        fail: reject
      });
    });
  }

  /**
   * 根据文件名获取Content-Type
   */
  getContentType(fileName) {
    const ext = fileName.split('.').pop().toLowerCase();
    const mimeTypes = {
      jpg: 'image/jpeg',
      jpeg: 'image/jpeg',
      png: 'image/png',
      gif: 'image/gif',
      webp: 'image/webp',
      bmp: 'image/bmp',
      mp4: 'video/mp4',
      avi: 'video/x-msvideo',
      mov: 'video/quicktime',
      mp3: 'audio/mpeg',
      wav: 'audio/wav',
      pdf: 'application/pdf',
      txt: 'text/plain',
      json: 'application/json'
    };

    return mimeTypes[ext] || 'application/octet-stream';
  }

  /**
   * 删除文件
   */
  deleteObject(key) {
    return new Promise((resolve, reject) => {
      if (!this.isInitialized) {
        this.init();
      }

      this.cosInstance.deleteObject({
        Bucket: config.cos.bucket,
        Region: config.cos.region,
        Key: key
      }, (err, data) => {
        if (err) {
          console.error('[COS] 删除文件失败:', err);
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  }

  /**
   * 获取文件访问URL
   */
  getObjectUrl(key, options = {}) {
    if (!this.isInitialized) {
      this.init();
    }

    return this.cosInstance.getObjectUrl({
      Bucket: config.cos.bucket,
      Region: config.cos.region,
      Key: key,
      Sign: options.sign !== false,
      Expires: options.expires || 3600
    });
  }

  /**
   * 专门针对10KB以内压缩的上传方法
   * @param {string} filePath - 文件路径
   * @param {Object} options - 选项
   * @returns {Promise<Object>} 上传结果
   */
  async uploadWithUltraCompression(filePath, options = {}) {
    return this.upload(filePath, {
      fileType: 'image',
      enableCompress: true,
      enableDedup: options.enableDedup || false,
      compressOptions: {
        maxWidth: 400,
        maxHeight: 400,
        targetSize: 20 * 1024, // 10KB
        ...options
      }
    });
  }
}

// 导出单例
const cosManager = new COSManager();
module.exports = cosManager;