/**
 * 重试工具函数
 * @description 提供通用的重试机制，支持指数退避和自定义重试条件
 */

/**
 * 重试选项配置
 */
export interface RetryOptions {
  /** 最大重试次数，默认 3 */
  maxRetries?: number
  /** 初始延迟时间（毫秒），默认 2000 */
  initialDelayMs?: number
  /** 是否使用指数退避，默认 true */
  useExponentialBackoff?: boolean
  /** 最大延迟时间（毫秒），默认 30000 */
  maxDelayMs?: number
  /** 自定义重试条件函数，返回 true 表示应该重试 */
  shouldRetry?: (error: any, attempt: number) => boolean
  /** 重试前的回调函数 */
  onRetry?: (error: any, attempt: number, delay: number) => void
}

/**
 * 默认重试条件 - 检查是否是 rate limiting 错误
 * @param error 错误对象
 * @returns 是否应该重试
 */
export function defaultShouldRetry(error: any): boolean {
  if (!error) return false
  const message = error?.message || String(error)
  // 检查 406 Not Acceptable、429 Too Many Requests 等 rate limiting 错误
  const isRateLimit =
    message.includes('406') ||
    message.includes('429') ||
    message.includes('Not Acceptable') ||
    message.includes('Too Many Requests') ||
    message.includes('rate limit')
  return isRateLimit
}

/**
 * 带重试机制的异步函数执行器
 *
 * @example
 * ```typescript
 * // 基本用法
 * const result = await withRetry(() => api.someRequest())
 *
 * // 自定义重试次数和延迟
 * const result = await withRetry(() => api.request(), {
 *   maxRetries: 5,
 *   initialDelayMs: 1000,
 *   useExponentialBackoff: true
 * })
 *
 * // 自定义重试条件
 * const result = await withRetry(() => api.request(), {
 *   shouldRetry: (error) => error?.code === 'NETWORK_ERROR'
 * })
 *
 * // 带重试回调
 * const result = await withRetry(() => api.request(), {
 *   onRetry: (error, attempt, delay) => {
 *     console.log(`第 ${attempt} 次重试，等待 ${delay}ms`, error.message)
 *   }
 * })
 * ```
 *
 * @param fn - 要执行的异步函数
 * @param options - 重试选项配置
 * @returns Promise resolves with the result of the function
 * @throws 如果重试次数用尽，抛出最后一次的错误
 */
export async function withRetry<T>(
  fn: () => Promise<T>,
  options: RetryOptions = {},
): Promise<T> {
  const {
    maxRetries = 3,
    initialDelayMs = 2000,
    useExponentialBackoff = true,
    maxDelayMs = 30000,
    shouldRetry = defaultShouldRetry,
    onRetry,
  } = options

  let lastError: any = null
  let currentDelay = initialDelayMs

  for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
    try {
      // 执行目标函数
      return await fn()
    } catch (error: any) {
      lastError = error

      // 判断是否应该重试
      const shouldRetryAttempt = shouldRetry(error, attempt)

      // 如果是最后一次尝试 或者 不应该重试，则抛出错误
      if (attempt > maxRetries || !shouldRetryAttempt) {
        if (attempt > maxRetries) {
          console.error(
            `❌ [withRetry] 已达到最大重试次数 (${maxRetries})，放弃请求`,
          )
        }
        throw error
      }

      // 执行重试回调
      if (onRetry) {
        onRetry(error, attempt, currentDelay)
      }

      // 打印日志
      console.log(
        `⏳ [withRetry] 第 ${attempt} 次重试，等待 ${currentDelay}ms...`,
        error?.message || String(error),
      )

      // 等待延迟时间
      await new Promise((resolve) => setTimeout(resolve, currentDelay))

      // 计算下一次延迟时间（指数退避）
      if (useExponentialBackoff) {
        currentDelay = Math.min(currentDelay * 2, maxDelayMs)
      }
    }
  }

  //理论上不会到达这里，因为循环内会 throw
  throw lastError
}

/**
 * 简化的重试装饰器 - 适用于类方法
 *
 * @example
 * ```typescript
 * class MyService {
 *   @retryable({ maxRetries: 3, initialDelayMs: 1000 })
 *   async fetchData() {
 *     return await api.request()
 *   }
 * }
 * ```
 *
 * @param options - 重试选项配置
 * @returns 装饰器函数
 */
export function retryable(options: RetryOptions = {}) {
  return function <T extends (...args: any[]) => Promise<any>>(
    _target: any,
    _propertyKey: string,
    descriptor: TypedPropertyDescriptor<T>,
  ) {
    const originalMethod = descriptor.value
    if (!originalMethod) return descriptor

    descriptor.value = function (this: any, ...args: Parameters<T>) {
      return withRetry(() => originalMethod.apply(this, args), options)
    } as T

    return descriptor
  } as MethodDecorator
}
