/**
 * 请求去重管理模块
 *
 * 支持两种去重策略：
 * - reject: 拒绝重复请求（生产环境默认，适用于表单提交等写操作）
 * - reuse: 复用请求结果（开发环境默认，适用于下拉框数据、列表查询等读操作）
 *
 * 开发环境下默认使用 reuse 策略，避免热更新（HMR）导致的重复请求问题
 * 生产环境下默认使用 reject 策略，严格防止重复提交
 *
 * @example
 * // 开发环境：自动使用 reuse 策略（热更新友好）
 * // 生产环境：自动使用 reject 策略（严格防护）
 * post('/api/save', data)
 *
 * // 显式指定策略（覆盖默认行为）
 * post('/api/options', params, { dedupe: true, dedupeStrategy: 'reuse' })
 * post('/api/submit', data, { dedupe: true, dedupeStrategy: 'reject' })
 *
 * // 关闭去重
 * post('/api/query', params, { dedupe: false })
 */

// ============ 去重策略常量 ============
export const DEDUPE_STRATEGY = {
  REJECT: 'reject', // 拒绝重复请求
  REUSE: 'reuse' // 复用请求结果
}

// 是否为开发环境
const isDev = process.env.NODE_ENV === 'development'

// 日志输出（不输出完整 URL 避免敏感信息泄露）
const logger = {
  warn: (msg, url) => console.warn(`[请求去重] ${msg}`, url ? `(${getUrlPath(url)})` : ''),
  log: (msg, url) => console.log(`[请求去重] ${msg}`, url ? `(${getUrlPath(url)})` : '')
}

// 提取 URL 路径部分，去除查询参数（可能包含敏感信息）
function getUrlPath (url) {
  if (!url) return ''
  try {
    // 只保留路径部分，去除查询参数
    return url.split('?')[0]
  } catch (e) {
    return '[URL解析失败]'
  }
}

// ============ 存储进行中的请求 ============
// Map<requestKey, { controller, config, promise?, resolvers? }>
const pendingRequests = new Map()

/**
 * 生成请求唯一标识
 * @param {object} config - axios 请求配置
 * @returns {string} 请求标识
 */
export function generateRequestKey(config) {
  const { method, url, data, params } = config
  let dataStr = ''
  let paramsStr = ''
  try {
    dataStr = data ? JSON.stringify(data) : ''
    paramsStr = params ? JSON.stringify(params) : ''
  } catch (e) {
    // 循环引用或其他序列化问题，使用时间戳保证唯一性
    logger.warn('参数序列化失败，跳过去重')
    dataStr = `_${Date.now()}_${Math.random()}`
  }
  return `${method}:${url}:${dataStr}:${paramsStr}`
}

/**
 * 检查是否有相同的请求正在进行
 * @param {string} requestKey - 请求标识
 * @returns {boolean}
 */
export function hasPendingRequest(requestKey) {
  return pendingRequests.has(requestKey)
}

/**
 * 获取 pending 请求信息
 * @param {string} requestKey - 请求标识
 * @returns {object|undefined}
 */
export function getPendingRequest(requestKey) {
  return pendingRequests.get(requestKey)
}

/**
 * 添加请求到 pending 列表
 * @param {string} requestKey - 请求标识
 * @param {object} config - axios 请求配置
 * @param {string} strategy - 去重策略
 * @returns {object} { controller, isReuse, promise? }
 */
export function addPendingRequest(requestKey, config, strategy = DEDUPE_STRATEGY.REJECT) {
  // 创建 AbortController 用于取消请求
  const controller = new AbortController()

  const pendingInfo = {
    controller,
    config,
    strategy,
    promise: null,
    resolvers: null
  }

  // 如果是复用策略，创建一个可共享的 Promise
  if (strategy === DEDUPE_STRATEGY.REUSE) {
    let _resolve, _reject
    pendingInfo.promise = new Promise((resolve, reject) => {
      _resolve = resolve
      _reject = reject
    })
    pendingInfo.resolvers = { resolve: _resolve, reject: _reject }
  }

  pendingRequests.set(requestKey, pendingInfo)

  return {
    controller,
    isReuse: false,
    signal: controller.signal
  }
}

/**
 * 处理重复请求
 * @param {string} requestKey - 请求标识
 * @param {object} config - axios 请求配置
 * @returns {object} { shouldProceed, promise?, error? }
 */
export function handleDuplicateRequest(requestKey, config) {
  const pending = pendingRequests.get(requestKey)

  if (!pending) {
    return { shouldProceed: true }
  }

  const strategy = config.dedupeStrategy || pending.strategy || DEDUPE_STRATEGY.REJECT

  // 复用策略：返回相同的 Promise
  if (strategy === DEDUPE_STRATEGY.REUSE && pending.promise) {
    logger.log('复用进行中的请求', config.url)
    return {
      shouldProceed: false,
      isReuse: true,
      promise: pending.promise
    }
  }

  // 拒绝策略：返回错误
  logger.warn('重复请求被拦截', config.url)
  const error = new Error('重复请求被取消')
  error.code = 'ERR_DUPLICATE_REQUEST'
  error.config = config
  return {
    shouldProceed: false,
    isReuse: false,
    error
  }
}

/**
 * 请求成功时的处理
 * @param {string} requestKey - 请求标识
 * @param {any} data - 响应数据
 */
export function resolvePendingRequest(requestKey, data) {
  const pending = pendingRequests.get(requestKey)
  if (pending?.resolvers) {
    pending.resolvers.resolve(data)
  }
  pendingRequests.delete(requestKey)
}

/**
 * 请求失败时的处理
 * @param {string} requestKey - 请求标识
 * @param {Error} error - 错误信息
 */
export function rejectPendingRequest(requestKey, error) {
  const pending = pendingRequests.get(requestKey)
  if (pending?.resolvers) {
    pending.resolvers.reject(error)
  }
  pendingRequests.delete(requestKey)
}

/**
 * 移除已完成的请求（兼容旧逻辑）
 * @param {string} requestKey - 请求标识
 */
export function removePendingRequest(requestKey) {
  pendingRequests.delete(requestKey)
}

/**
 * 清空所有 pending 请求
 */
export function clearPendingRequests() {
  pendingRequests.forEach(({ controller, resolvers }) => {
    controller.abort()
    // 如果有等待复用的请求，也要拒绝它们
    if (resolvers) {
      const error = new Error('请求被取消')
      error.code = 'ERR_CANCELED'
      resolvers.reject(error)
    }
  })
  pendingRequests.clear()
}

/**
 * 获取当前 pending 请求数量（用于调试）
 * @returns {number}
 */
export function getPendingCount() {
  return pendingRequests.size
}

/**
 * 判断是否应该启用去重
 * @param {object} config - axios 请求配置
 * @returns {boolean}
 */
export function shouldEnableDedupe(config) {
  // 显式关闭去重
  if (config.dedupe === false) {
    return false
  }
  // 显式开启去重
  if (config.dedupe === true) {
    return true
  }
  // 默认：POST 请求开启去重
  return config.method?.toLowerCase() === 'post'
}

/**
 * 获取去重策略
 * @param {object} config - axios 请求配置
 * @returns {string}
 *
 * 开发环境下默认使用 reuse 策略，避免热更新导致的重复请求问题
 */
export function getDedupeStrategy(config) {
  // 如果显式指定了策略，优先使用
  if (config.dedupeStrategy) {
    return config.dedupeStrategy
  }
  // 开发环境下默认使用复用策略，避免热更新重复请求
  if (isDev) {
    return DEDUPE_STRATEGY.REUSE
  }
  // 生产环境默认使用拒绝策略
  return DEDUPE_STRATEGY.REJECT
}
