// src/utils/imageCacheManager.js
import { IMAGE_CACHE_VERSION } from '@/config/imageCacheVersion'
import { OSS_REMOTE_BASE_URL } from '@/config/imagePaths.js'
import { resolveProtectedAssetUrl } from '@/utils/protectedAssets.js'
import { useDebounce } from '@/utils/useDebounce'

class ImageCacheManager {
  constructor() {
    this.cacheVersionKey = 'h5_app_version'
    this.currentAppVersion = import.meta.env.VITE_APP_VERSION || '1.0.0'
    this.currentImageVersion = IMAGE_CACHE_VERSION.version // 使用完整版本号
    // 移除了 this.versionType = import.meta.env.VITE_VERSION_TYPE || 'patch'

    this.imageCacheName = `likei-images-v${this.currentImageVersion}` // OSS图片
    this.assetCacheName = `likei-assets-v${this.currentImageVersion}` // 本地assets

    this.maxCacheItems = IMAGE_CACHE_VERSION.cacheLimits?.maxItems || 120
    this.maxCacheSize = (IMAGE_CACHE_VERSION.cacheLimits?.maxSizeMB || 80) * 1024 * 1024
    this.currentImageCacheSize = 0 // 当前image缓存大小（字节）
    this.currentAssetCacheSize = 0 // 当前asset缓存大小（字节）

    this.requestScheduler = new Map() // 上层请求调度器： CORS 降级处理 （Promise 或 error）
    this.pendingRequests = new Map() // 底层请求管理：包含重试次数 { url: { promise, retries } }
    this.maxRetries = 3 // 最大重试次数

    // ✅ 新增：防抖版本的缓存限制检查（1 秒防抖，合并多次调用）
    this.enforceCacheLimitsDebounced = useDebounce(
      this.enforceCacheLimits.bind(this), // ⚠️ 只绑定 this，不绑定参数
      1000,
      false,
    )

    this.init()
  }

  // 解析版本号为数组 [major, minor, patch]
  parseVersion(version) {
    return version.split('.').map(Number)
  }

  // 比较两个版本号，返回是否版本发生了变化（任何部分的变化）
  isVersionChanged(cachedVersion, currentVersion) {
    return cachedVersion !== currentVersion
  }

  // 检查是否是major版本更新
  isMajorVersionUpdate(cachedVersion, currentVersion) {
    const [cachedMajor] = this.parseVersion(cachedVersion)
    const [currentMajor] = this.parseVersion(currentVersion)
    return currentMajor > cachedMajor
  }

  getCacheStorage() {
    if (typeof window === 'undefined') return null
    return window.caches || null
  }

  // 返回对应的缓存
  async getCacheForUrl(url) {
    // 规范化 URL
    const normalizedUrl = this.normalizeUrl(url)

    let cacheName
    if (
      normalizedUrl.startsWith(OSS_REMOTE_BASE_URL) ||
      normalizedUrl.includes('/oss/h5/Aslan/')
    ) {
      cacheName = this.imageCacheName
    } else if (normalizedUrl.includes('/assets/')) {
      cacheName = this.assetCacheName
    } else {
      // 其他外部链接放入通用图片缓存
      cacheName = this.imageCacheName
    }

    if (!this.getCacheStorage()) {
      return { cache: null, cacheName }
    }

    try {
      const cache = await this.getCacheStorage().open(cacheName)
      return { cache, cacheName } // 返回包含缓存和名称的对象
    } catch (error) {
      console.error(`打开缓存失败: ${cacheName}`, error)
      throw error
    }
  }

  async init() {
    await this.checkVersionAndUpdate()
  }

  // 版本检查逻辑
  async checkVersionAndUpdate() {
    const cachedVersion = localStorage.getItem(this.cacheVersionKey)
    const cachedImageVersion = localStorage.getItem('likei-image-version')

    if (!cachedVersion) {
      // 首次安装：保存当前版本和图片版本
      localStorage.setItem(this.cacheVersionKey, this.currentAppVersion)
      localStorage.setItem('likei-image-version', IMAGE_CACHE_VERSION.version)
      return
    }

    // 检查是否是major版本更新
    const isMajorUpdate = this.isMajorVersionUpdate(cachedVersion, this.currentAppVersion)

    // major版本更新：清除所有缓存
    if (isMajorUpdate) {
      console.log(`major版本更新：清除所有缓存`)
      await this.clearAllCaches()
    }

    // 非major版本更新：检查图片版本是否变化
    else {
      const isImageVersionChanged = this.isVersionChanged(
        cachedImageVersion || '1.0.0',
        IMAGE_CACHE_VERSION.version,
      )

      // 图片版本变化：根据配置清除指定类型的缓存
      if (isImageVersionChanged) {
        console.log(`非major版本更新：清除缓存类型: ${IMAGE_CACHE_VERSION.cacheType}`)
        await this.clearCacheByType(IMAGE_CACHE_VERSION.cacheType)
      }
      // 如果图片版本未变化且不是major更新，则不执行任何清除操作
    }

    // 更新版本号
    localStorage.setItem(this.cacheVersionKey, this.currentAppVersion)
    localStorage.setItem('likei-image-version', IMAGE_CACHE_VERSION.version)
  }

  // 根据类型清除缓存
  async clearCacheByType(cacheType) {
    switch (cacheType) {
      case 'images':
        await this.clearOldImageCaches()
        break
      case 'assets':
        await this.clearOldAssetCaches()
        break
      case 'all':
        await this.clearAllCaches()
        break
      default:
        // 默认清除全部图片缓存
        await this.clearAllCaches()
        break
    }
  }

  // 更新指定缓存的大小
  _updateCacheSize(cacheName, deltaSize) {
    if (cacheName === this.imageCacheName) {
      this.currentImageCacheSize += deltaSize
    } else if (cacheName === this.assetCacheName) {
      this.currentAssetCacheSize += deltaSize
    }
  }

  // 检查并清理超出限制的缓存
  async enforceCacheLimits(cache, cacheName) {
    try {
      // 快速判断：根据缓存名称选择对应的大小属性
      let currentCacheSize
      if (cacheName === this.imageCacheName) {
        currentCacheSize = this.currentImageCacheSize
      } else if (cacheName === this.assetCacheName) {
        currentCacheSize = this.currentAssetCacheSize
      } else {
        console.warn(`未知缓存名称: ${cacheName}`)
        return
      }

      // 如果当前缓存大小未超过限制，直接返回
      if (currentCacheSize <= this.maxCacheSize) {
        const keys = await cache.keys()
        if (keys.length <= this.maxCacheItems) {
          return
        }
      }

      // 超限情况：获取详细缓存项信息并清理
      const keys = await cache.keys()
      const cacheEntries = []

      for (const key of keys) {
        try {
          const response = await cache.match(key)
          if (response) {
            // ✅ 优化：优先使用 Content-Length 头，避免 blob() 开销
            const contentLength = response.headers.get('content-length')
            // 使用与添加时相同的兜底逻辑
            const size = contentLength ? parseInt(contentLength, 10) : (await response.blob()).size // 兜底方案
            cacheEntries.push({ key, size, response })
          }
        } catch (e) {
          console.warn('无法获取缓存项大小:', key.url)
        }
      }

      let totalItems = cacheEntries.length
      let totalSize = cacheEntries.reduce((sum, entry) => sum + entry.size, 0)

      let itemsToRemoveCount = Math.max(0, totalItems - this.maxCacheItems)
      let sizeToRemove = Math.max(0, totalSize - this.maxCacheSize)

      let itemsToDelete = []

      if (itemsToRemoveCount > 0 || sizeToRemove > 0) {
        let removedSize = 0
        let removedCount = 0

        for (const entry of cacheEntries) {
          if (removedCount >= itemsToRemoveCount && totalSize - removedSize <= this.maxCacheSize) {
            break
          }

          itemsToDelete.push(entry.key)
          removedSize += entry.size
          removedCount++
        }
      }

      // 执行删除操作并更新缓存大小
      for (const key of itemsToDelete) {
        const response = await cache.match(key)
        if (response) {
          const contentLength = response.headers.get('content-length')
          if (contentLength) {
            // 使用与添加时相同的逻辑
            const size = contentLength ? parseInt(contentLength, 10) : (await response.blob()).size // 兜底
            this._updateCacheSize(cacheName, -size) // 减少缓存大小
          }
        }
        await cache.delete(key)
      }
    } catch (error) {
      console.warn('检查缓存限制时出错:', error)
    }
  }

  // 清除旧的OSS图片缓存
  async clearOldImageCaches() {
    const cacheStorage = this.getCacheStorage()
    if (!cacheStorage) return

    try {
      const cacheNames = await cacheStorage.keys()
      const oldImageCaches = cacheNames.filter(
        (name) =>
          name.startsWith('likei-images-v') && !name.includes(`v${this.currentImageVersion}`), // 排除当前版本的缓存
      )

      console.log('正在清除旧的OSS图片缓存:', oldImageCaches)
      await Promise.all(oldImageCaches.map((name) => cacheStorage.delete(name)))
      this.currentImageCacheSize = 0 // 重置计数器
    } catch (error) {
      console.warn('清理旧的OSS图片缓存失败:', error)
    }
  }

  // 清除旧的本地assets缓存
  async clearOldAssetCaches() {
    const cacheStorage = this.getCacheStorage()
    if (!cacheStorage) return

    try {
      const cacheNames = await cacheStorage.keys()
      const oldAssetCaches = cacheNames.filter(
        (name) =>
          name.startsWith('likei-assets-v') && !name.includes(`v${this.currentImageVersion}`), // 排除当前版本的缓存
      )

      console.log('正在清除旧的本地assets缓存:', oldAssetCaches)
      await Promise.all(oldAssetCaches.map((name) => cacheStorage.delete(name)))
      this.currentAssetCacheSize = 0 // 重置计数器
    } catch (error) {
      console.warn('清理旧的本地assets缓存失败:', error)
    }
  }

  // 清除所有图片缓存（OSS图片、本地assets图片）
  async clearAllCaches() {
    const cacheStorage = this.getCacheStorage()
    if (!cacheStorage) return

    try {
      const cacheNames = await cacheStorage.keys()
      const allCaches = cacheNames.filter(
        (name) => name.startsWith('likei-images-v') || name.startsWith('likei-assets-v'),
      )

      console.log('正在清除所有图片缓存:', allCaches)
      await Promise.all(allCaches.map((name) => cacheStorage.delete(name)))
      // 重置计数器
      this.currentImageCacheSize = 0
      this.currentAssetCacheSize = 0
    } catch (error) {
      console.warn('清理所有图片缓存失败:', error)
    }
  }

  // 判断是否为适合缓存资源
  isBuildAsset(url) {
    try {
      // 规范化 URL
      const normalizedUrl = this.normalizeUrl(url)

      const containsAssets = normalizedUrl.includes('/assets/')
      const containsOssH5 = normalizedUrl.includes('/oss/h5/Aslan/')
      const startsWithOss = normalizedUrl.startsWith(OSS_REMOTE_BASE_URL)

      // 只缓存三种类型的资源
      return containsAssets || containsOssH5 || startsWithOss
    } catch (error) {
      console.warn('判断资源类型时出错:', error)
      return false
    }
  }

  // 规范化 URL 以确保一致性
  normalizeUrl(url) {
    try {
      const resolvedUrl = resolveProtectedAssetUrl(url)

      // 如果是相对路径，转换为完整 URL 进行匹配
      if (resolvedUrl.startsWith('/')) {
        return new URL(resolvedUrl, window.location.origin).toString()
      }
      // 如果已经是完整 URL，直接返回
      return new URL(resolvedUrl).toString()
    } catch (e) {
      // 如果 URL 格式不正确，返回原 URL
      return resolveProtectedAssetUrl(url)
    }
  }

  // 从缓存获取图片
  async getCachedImage(url) {
    if (!this.getCacheStorage()) return null

    try {
      // 规范化 URL
      const normalizedUrl = this.normalizeUrl(url)

      // 只从缓存获取本地资源
      if (this.isBuildAsset(normalizedUrl)) {
        const { cache, cacheName } = await this.getCacheForUrl(normalizedUrl) // 选中对应缓存
        if (!cache) return null

        // 尝试多种匹配方式
        // 带配置的 Request 对象匹配：严格匹配具有特定 CORS 配置和凭证策略的缓存响应，确保请求模式一致
        let cachedResponse = await cache.match(
          new Request(normalizedUrl, {
            mode: 'cors',
            credentials: 'omit',
          }),
        )

        // URL 字符串精确匹配：最基础的匹配方式，查找缓存中 URL 完全一致的请求记录
        if (!cachedResponse) {
          cachedResponse = await cache.match(normalizedUrl)
        }

        // 只允许无版本参数的 URL 回退匹配无 query 缓存。
        // 带 iv 的版本化资源不能回退命中旧的无版本缓存。
        if (!cachedResponse && !new URL(normalizedUrl).search) {
          const urlObj = new URL(normalizedUrl)
          const urlWithoutQuery = `${urlObj.origin}${urlObj.pathname}`
          cachedResponse = await cache.match(urlWithoutQuery)
        }

        return cachedResponse
      }
      return null
    } catch (error) {
      console.warn('获取缓存图片失败:', error)
      return null
    }
  }

  // 统一的图片加载方法
  async loadImage(src) {
    const normalizedSrc = this.normalizeUrl(src)

    // 1. 检查是否有正在进行的请求
    if (this.pendingRequests.has(normalizedSrc)) {
      return this.pendingRequests.get(normalizedSrc).promise
    }

    // 2. 检查缓存中是否存在图片
    const cachedResponse = await this.getCachedImage(normalizedSrc)
    if (cachedResponse) {
      const blob = await cachedResponse.blob()
      return URL.createObjectURL(blob)
    }

    // 3. 请求和缓存都没有
    // （1）创建新的请求并记录到 pendingRequests
    const loadPromise = this.createLoadPromise(normalizedSrc)
    this.pendingRequests.set(normalizedSrc, {
      promise: loadPromise,
      retries: this.maxRetries, // 记录重试次数
    })
    // （2）执行请求
    try {
      const result = await loadPromise
      return result
    } finally {
      // 无论成功或失败，都要清理 pendingRequests
      this.pendingRequests.delete(normalizedSrc)
    }
  }

  // 上层请求调度器：确保所有请求统一管理
  async scheduleRequest(src) {
    const normalizedSrc = this.normalizeUrl(src)

    // 如果已经在调度器中，则直接返回
    if (this.requestScheduler.has(normalizedSrc)) {
      const result = this.requestScheduler.get(normalizedSrc)
      if (result instanceof Promise) {
        try {
          return await result
        } catch (error) {
          // 如果是 CORS 错误，直接返回原始 URL
          if (error.message.includes('CORS') || error.name === 'TypeError') {
            console.warn(`CORS 错误，跳过重试: ${src}`)
            return src
          }
          throw error // 其他错误继续抛出
        }
      }
      return result
    }

    // 否则创建新请求并加入调度器
    const requestPromise = this.loadImage(src)
    this.requestScheduler.set(normalizedSrc, requestPromise) // 不记录重试次数，只存 Promise

    try {
      return await requestPromise
    } finally {
      this.requestScheduler.delete(normalizedSrc) // 清理调度器中的记录
    }
  }

  // 创建加载 Promise（含重试逻辑）
  async createLoadPromise(src) {
    // 冗余步骤（作为防御性编程）
    // const normalizedSrc = this.normalizeUrl(src)

    // // 新增：检查是否已有相同请求正在进行
    // if (this.pendingRequests.has(normalizedSrc)) {
    //   console.warn(`请求已存在，跳过重复加载: ${src}`)
    //   return this.pendingRequests.get(normalizedSrc).promise
    // }

    const attemptLoad = async (retriesLeft) => {
      try {
        const response = await fetch(src, {
          mode: 'cors',
          credentials: 'omit',
          cache: 'default',
        })

        if (!response.ok) {
          throw new Error(`HTTP ${response.status}`)
        }

        if (this.isBuildAsset(src)) {
          const { cache, cacheName } = await this.getCacheForUrl(src)
          if (!cache) {
            const blob = await response.blob()
            return URL.createObjectURL(blob)
          }
          const clonedResponse = response.clone() // 克隆响应用于缓存
          await cache.put(src, clonedResponse) // 写入缓存

          // 获取资源大小并更新缓存大小
          const contentLength = response.headers.get('content-length')
          if (contentLength) {
            const size = parseInt(contentLength, 10)
            this._updateCacheSize(cacheName, size) // 更新缓存大小
          }

          // 异步检查缓存限制
          // ✅ 修复：包装为 Promise 确保错误捕获
          // 等价于enforceCacheLimits.call(this, cache, cacheName)
          Promise.resolve(this.enforceCacheLimitsDebounced(cache, cacheName)).catch((error) => {
            console.warn('检查缓存限制时出错:', error)
          })
        }

        const blob = await response.blob()
        return URL.createObjectURL(blob)
      } catch (error) {
        // 如果是 CORS 错误，直接终止重试
        if (error.message.includes('CORS') || error.name === 'TypeError') {
          console.warn(`CORS 错误，终止重试: ${src}`)
          throw new Error(`CORS blocked: ${src}`)
        }

        // 其他错误继续重试
        if (retriesLeft > 0) {
          console.warn(`图片加载失败，剩余重试次数: ${retriesLeft}`, src, error)
          return attemptLoad(retriesLeft - 1)
        } else {
          throw new Error(`图片加载失败且无重试机会: ${src}`)
        }
      }
    }

    return attemptLoad(this.maxRetries)
  }
}

// 导出单例
export const imageCacheManager = new ImageCacheManager()
