/**
 * 统一请求封装
 *
 * - 自动附加 Authorization: Bearer <token>
 * - 401 时自动尝试 refresh_token 刷新，刷新成功后重试原请求
 * - 刷新失败时清除 token 并跳转 login 页面
 * - BASE_URL 从 config.ts 读取（按运行环境自动切换）
 */

import { API_BASE } from "./config"

export interface RequestOptions {
  url: string
  method?: "GET" | "POST" | "PUT" | "DELETE"
  data?: any
  header?: Record<string, string>
  /** 是否需要认证，默认 true */
  needAuth?: boolean
}

/** 是否正在刷新 token，防止并发刷新 */
let isRefreshing = false
/** 刷新期间排队的请求（等刷新完成后统一重试） */
let pendingQueue: Array<{
  resolve: (value: any) => void
  reject: (reason: any) => void
  options: RequestOptions
}> = []

/**
 * 从 globalData 或 Storage 获取 access_token
 */
function getAccessToken(): string | undefined {
  const app = getApp<IAppOption>()
  return app.globalData.token || wx.getStorageSync("token") || undefined
}

/**
 * 从 globalData 或 Storage 获取 refresh_token
 */
function getRefreshToken(): string | undefined {
  const app = getApp<IAppOption>()
  return app.globalData.refreshToken || wx.getStorageSync("refreshToken") || undefined
}

/**
 * 清除本地存储的所有 token
 */
function clearTokens(): void {
  const app = getApp<IAppOption>()
  app.globalData.token = undefined
  app.globalData.refreshToken = undefined
  wx.removeStorageSync("token")
  wx.removeStorageSync("refreshToken")
}

/**
 * 保存 token 到 globalData 和 Storage
 */
function saveTokens(accessToken: string, refreshToken: string): void {
  const app = getApp<IAppOption>()
  app.globalData.token = accessToken
  app.globalData.refreshToken = refreshToken
  wx.setStorageSync("token", accessToken)
  wx.setStorageSync("refreshToken", refreshToken)
}

/**
 * 跳转到登录页（使用 reLaunch 清空页面栈）
 */
function redirectToLogin(): void {
  wx.reLaunch({ url: "/pages/login/login" })
}

/**
 * 执行底层 wx.request，返回 Promise
 */
function wxRequest(options: RequestOptions): Promise<any> {
  return new Promise((resolve, reject) => {
    wx.request({
      url: `${API_BASE}${options.url}`,
      method: options.method || "GET",
      data: options.data,
      header: options.header || {},
      success(res) {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          resolve(res.data)
        } else {
          reject({ statusCode: res.statusCode, data: res.data })
        }
      },
      fail(err) {
        reject({ statusCode: 0, data: err })
      },
    })
  })
}

/**
 * 尝试用 refresh_token 刷新令牌
 *
 * 成功 → 保存新 token 并返回 true
 * 失败 → 清除 token、跳转登录页、返回 false
 */
async function tryRefreshToken(): Promise<boolean> {
  const rt = getRefreshToken()
  if (!rt) {
    clearTokens()
    redirectToLogin()
    return false
  }

  try {
    const data = await wxRequest({
      url: "/api/xcx/refresh",
      method: "POST",
      data: { refresh_token: rt },
      header: { "Content-Type": "application/json" },
      needAuth: false,
    })
    if (data.access_token && data.refresh_token) {
      saveTokens(data.access_token, data.refresh_token)
      return true
    }
    // 响应格式异常，视为刷新失败
    clearTokens()
    redirectToLogin()
    return false
  } catch {
    clearTokens()
    redirectToLogin()
    return false
  }
}

/**
 * 处理排队中的请求：刷新成功后全部重试，失败则全部拒绝
 */
function flushPendingQueue(success: boolean): void {
  const queue = [...pendingQueue]
  pendingQueue = []
  for (const item of queue) {
    if (success) {
      // 重试时会自动附加新 token
      request(item.options).then(item.resolve, item.reject)
    } else {
      item.reject({ statusCode: 401, data: { detail: "刷新令牌失败" } })
    }
  }
}

/**
 * 统一请求入口
 *
 * @param options 请求配置
 * @returns 响应数据（已解析的 JSON）
 */
export function request(options: RequestOptions): Promise<any> {
  const needAuth = options.needAuth !== false
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
    ...options.header,
  }

  // 自动附加 Authorization header
  if (needAuth) {
    const token = getAccessToken()
    if (token) {
      headers["Authorization"] = `Bearer ${token}`
    }
  }

  const finalOptions: RequestOptions = { ...options, header: headers }

  return wxRequest(finalOptions).catch(async (err) => {
    // 非 401 或不需要认证的请求，直接抛出
    if (err.statusCode !== 401 || !needAuth) {
      throw err
    }

    // 401 → 尝试刷新 token
    if (isRefreshing) {
      // 已有刷新请求在进行中，排队等待
      return new Promise((resolve, reject) => {
        pendingQueue.push({ resolve, reject, options })
      })
    }

    isRefreshing = true
    try {
      const ok = await tryRefreshToken()
      flushPendingQueue(ok)
      if (!ok) {
        throw { statusCode: 401, data: { detail: "刷新令牌失败" } }
      }
      // 刷新成功，用新 token 重试原请求
      return request(options)
    } finally {
      isRefreshing = false
    }
  })
}

export default request
