// =============================================================================
// 企微IT智能服务台 — H5用户端 Axios 实例与拦截器
// =============================================================================
// 说明：创建 Axios 实例，配置：
//   1. 请求基础 URL
//   2. 请求拦截器（添加 Bearer Token 认证头）
//   3. 响应拦截器（统一错误处理 + 401 自动重新授权）
// =============================================================================

import axios from 'axios'
import type { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
// Vant 轻提示
import { showToast } from 'vant'
// Bug #2 修复：从独立回调模块导入，替代 dynamic import('@/stores/employee')，
// 打破 api/index.ts → stores/employee.ts 的循环依赖
import { triggerAuthExpired } from '@/utils/authCallback'

// --------------------------------------------------------------------------
// 创建 Axios 实例
// --------------------------------------------------------------------------
const apiClient: AxiosInstance = axios.create({
  // 基础 URL：所有请求会自动加上这个前缀
  baseURL: '/api',
  // 请求超时时间（20秒，原10秒）
  // 原因：图片/文件上传、AI消息处理等场景后端处理需要更多时间
  // 修复截图发送超时Bug
  timeout: 20000,
  // 默认请求头
  headers: {
    'Content-Type': 'application/json',
  },
})

// --------------------------------------------------------------------------
// 请求拦截器
// --------------------------------------------------------------------------
// 在每个请求发送前执行，用于添加 Bearer Token 认证头
apiClient.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 从 localStorage 获取 token，添加到 Authorization 头
    // 替换旧的 X-Employee-Id 明文头，使用 Bearer Token 进行安全认证
    const token = localStorage.getItem('h5_token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }

    // 兼容过渡：如果同时存在 employee_id 且无 token，则仍然发送 X-Employee-Id
    // 这确保了在 token 过期但 localStorage 中仍有旧数据的降级场景
    if (!token) {
      const employeeId = localStorage.getItem('employee_id')
      if (employeeId) {
        config.headers['X-Employee-Id'] = employeeId
      }
    }

    return config
  },
  (error) => {
    // 请求配置错误时直接返回
    return Promise.reject(error)
  }
)

// --------------------------------------------------------------------------
// 响应拦截器
// --------------------------------------------------------------------------
// 在每个响应返回后执行，用于统一处理错误
// 特殊处理 401：自动清除 token 并重新走 OAuth2 授权流程
apiClient.interceptors.response.use(
  (response: AxiosResponse) => {
    // 从响应中提取业务数据
    const res = response.data

    // 统一响应格式：{code: 0, data: {}, message: "success"}
    if (res.code !== 0) {
      // 特殊处理：业务码 1002 = 未授权（token 过期/无效）
      // 后端 _get_current_employee 在 Redis 查不到 token 时返回此码
      if (res.code === 1002) {
        handleAuthExpired('biz1002')
        return Promise.reject(new Error(res.message || '未授权'))
      }

      // 普通业务错误：显示轻提示
      showToast(res.message || '请求失败')
      return Promise.reject(new Error(res.message || '请求失败'))
    }

    // 业务成功：返回 response.data（即 {code, data, message} 包装对象）
    // API 函数通过 response.data 取出业务数据（与原始工作代码一致）
    return response.data
  },
  async (error) => {
    // 网络错误或服务器错误
    let message = '网络异常，请稍后重试'

    if (error.response) {
      switch (error.response.status) {
        case 401:
          // HTTP 401：Token 过期或无效（FastAPI 直接返回的 HTTP 状态码）
          await handleAuthExpired('http401')
          break
        case 403:
          message = '拒绝访问'
          break
        case 404:
          message = '请求的资源不存在'
          break
        case 500:
          message = '服务器内部错误'
          break
        default:
          message = `请求失败 (${error.response.status})`
      }
    } else if (error.code === 'ECONNABORTED') {
      message = '请求超时，请稍后重试'
    }

    // 显示轻提示（401 时不显示通用提示，因为会自动跳转授权）
    if (!error.response || error.response.status !== 401) {
      showToast(message)
    }

    return Promise.reject(error)
  }
)

// --------------------------------------------------------------------------
// 辅助：处理认证过期/未授权（复用逻辑）
// --------------------------------------------------------------------------
// 场景1: HTTP 401（Axios error 拦截器）
// 场景2: 业务码 1002 "未授权"（success 拦截器，后端 Redis token 过期时返回）
// 防循环机制：通过 localStorage 计数器限制 OAuth2 重定向次数

/** OAuth2 重定向计数器 key（与 employee store 保持一致） */
const OAUTH_REDIRECT_COUNT_KEY = 'oauth_redirect_count'
/** 最大允许重定向次数 */
const OAUTH_MAX_REDIRECT_COUNT = 3

// Bug #3 修复：401 去重锁，防止并发请求同时触发多次 OAuth2 重定向
// 当第一个 401 处理完成后，后续并发的 401 等待同一个 Promise 即可
let _authExpiredPromise: Promise<void> | null = null

async function handleAuthExpired(source: 'http401' | 'biz1002'): Promise<void> {
  const label = source === 'http401' ? 'HTTP 401' : '业务码 1002'

  // Bug #3 修复：如果已有 401 正在处理中，复用同一个 Promise，避免多次重定向
  if (_authExpiredPromise) {
    console.warn(`[API] ${label} 未授权 — 已有处理进行中，等待完成`)
    return _authExpiredPromise
  }

  console.warn(`[API] ${label} 未授权，清除凭证并跳转登录`)

  // 创建处理 Promise 并缓存（去重用）
  _authExpiredPromise = (async () => {
    try {
      // 清除本地 token
      localStorage.removeItem('h5_token')
      localStorage.removeItem('employee_id')
      localStorage.removeItem('employee_name')

      // 判断是 mock 模式还是生产模式
      const corpId = import.meta.env.VITE_WECOM_CORP_ID || ''
      if (!corpId) {
        // Mock 模式：跳转登录页
        showToast('登录已过期，请重新登录')
        // 避免重复跳转（当前已经在登录页时不再跳转）
        if (window.location.pathname !== '/itdesk/login') {
          window.location.href = '/itdesk/login'
        }
      } else {
        // 防循环检测：超过最大重定向次数时停止跳转
        const currentCount = parseInt(localStorage.getItem(OAUTH_REDIRECT_COUNT_KEY) || '0', 10)
        if (currentCount >= OAUTH_MAX_REDIRECT_COUNT) {
          console.error('[API] OAuth2 重定向次数超限，疑似无限循环，停止重定向')
          showToast('登录状态异常，请刷新页面重试')
          return
        }

        console.warn(`[API] OAuth2 重定向计数: ${currentCount}/${OAUTH_MAX_REDIRECT_COUNT}`)

        // Bug #2 修复：通过回调注册中心触发 store 的 handleUnauthorized，
        // 替代原来的 dynamic import('@/stores/employee')，消除循环依赖风险
        const triggered = await triggerAuthExpired()
        if (!triggered) {
          console.warn('[API] 认证过期处理器未注册，降级为刷新页面')
          window.location.reload()
        }
      }
    } catch (e) {
      console.warn('[API] 401 处理失败，刷新页面:', e)
      window.location.reload()
    } finally {
      // Bug #3 修复：处理完成后清除锁，允许未来的 401 重新触发
      _authExpiredPromise = null
    }
  })()

  return _authExpiredPromise
}

// 导出 Axios 实例
export default apiClient
