import axios, {
  type AxiosInstance,
  type AxiosError,
  type AxiosRequestConfig,
  type AxiosResponse,
  type InternalAxiosRequestConfig,
} from 'axios'
import { ElMessage } from 'element-plus'
import { getToken, setToken, removeToken } from '@/utils/auth'
import type { ApiResponse } from '@/types'

// 扩展 axios 请求配置，支持静默模式与请求取消
declare module 'axios' {
  interface AxiosRequestConfig {
    silent?: boolean
  }
}

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api'

const request: AxiosInstance = axios.create({
  baseURL: API_BASE_URL,
  timeout: 10000,
})

// 独立的刷新请求客户端，避免拦截器递归触发刷新
const refreshClient: AxiosInstance = axios.create({
  baseURL: API_BASE_URL,
  timeout: 10000,
})

function getRequestTimeout(config: InternalAxiosRequestConfig): number {
  if (config.url?.includes('/file/upload')) {
    return 120000
  }
  if (config.method === 'post' && config.data && typeof config.data === 'object') {
    const payload = JSON.stringify(config.data)
    if (payload.length > 1024 * 50) {
      return 30000
    }
  }
  return config.timeout || 10000
}

function attachToken(config: InternalAxiosRequestConfig) {
  const token = getToken()
  if (token && config.headers) {
    config.headers.Authorization = `Bearer ${token}`
  }
  config.timeout = getRequestTimeout(config)
  return config
}

request.interceptors.request.use(attachToken, (error: AxiosError) => Promise.reject(error))
refreshClient.interceptors.request.use(attachToken, (error: AxiosError) => Promise.reject(error))

let hasShownAuthError = false
let isRefreshing = false
let refreshPromise: Promise<string | null> | null = null

function handleUnauthorized() {
  if (hasShownAuthError) return
  hasShownAuthError = true
  removeToken()
  setTimeout(() => {
    window.location.href = '/login'
    hasShownAuthError = false
  }, 500)
}

async function syncStoreToken(token: string) {
  try {
    const { useUserStore } = await import('@/stores/user-store')
    const userStore = useUserStore()
    userStore.setToken(token)
  } catch {
    // store 未初始化时忽略
  }
}

async function doRefresh(): Promise<string | null> {
  const token = getToken()
  if (!token) return null
  try {
    const res = await refreshClient.post<ApiResponse<{ token: string }>>('/auth/refresh')
    if (res.data.code === 200 && res.data.data?.token) {
      const newToken = res.data.data.token
      setToken(newToken)
      await syncStoreToken(newToken)
      return newToken
    }
    return null
  } catch {
    return null
  }
}

async function tryRefresh(): Promise<string | null> {
  if (!isRefreshing) {
    isRefreshing = true
    refreshPromise = doRefresh().finally(() => {
      isRefreshing = false
      refreshPromise = null
    })
  }
  return refreshPromise!
}

async function retryWithNewToken(config: InternalAxiosRequestConfig, newToken: string): Promise<unknown> {
  const headers = { ...config.headers, Authorization: `Bearer ${newToken}` }
  return request({ ...config, headers })
}

async function handleTokenExpired(config?: InternalAxiosRequestConfig): Promise<never> {
  const newToken = await tryRefresh()
  if (!newToken) {
    handleUnauthorized()
    return Promise.reject(new Error('登录已过期，请重新登录'))
  }
  if (config) {
    return retryWithNewToken(config, newToken) as Promise<never>
  }
  return Promise.reject(new Error('登录已过期，请重新登录'))
}

request.interceptors.response.use(
  ((response: AxiosResponse<unknown>) => {
    // 二进制流直接返回，不做统一 code 校验
    if (response.config.responseType === 'blob' || response.config.responseType === 'arraybuffer') {
      return response.data
    }

    const res = response.data as ApiResponse<unknown>
    if (res.code !== 200) {
      const silent = response.config.silent ?? false
      if (!silent) {
        ElMessage.error(res.msg || '请求失败')
      }
      if (res.code === 401) {
        return handleTokenExpired(response.config)
      }
      return Promise.reject(new Error(res.msg || '请求失败'))
    }
    return res.data
  }) as any,
  (async (error: AxiosError) => {
    const status = error.response?.status
    const data = error.response?.data as { msg?: string } | undefined
    const silent = error.config?.silent ?? false
    let msg = data?.msg || error.message || '网络错误'

    if (status === 401) {
      if (error.config) {
        return handleTokenExpired(error.config)
      }
      msg = '登录已过期，请重新登录'
      handleUnauthorized()
    } else if (status === 403) {
      msg = '无权访问该资源'
    } else if (typeof status === 'number' && status >= 500) {
      msg = '服务器繁忙，请稍后重试'
    } else if (error.code === 'ECONNABORTED') {
      msg = '请求超时，请稍后重试'
    }

    if (!silent && (!hasShownAuthError || status !== 401)) {
      ElMessage.error(msg)
    }
    if (silent && error.response) {
      error.displayMsg = msg
    }
    return Promise.reject(error)
  }) as any
)

export interface RequestConfig extends AxiosRequestConfig {
  silent?: boolean
}

export const http = {
  get<T = unknown>(url: string, config?: RequestConfig): Promise<T> {
    return request.get<never, T>(url, config)
  },
  post<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<T> {
    return request.post<never, T>(url, data, config)
  },
  put<T = unknown>(url: string, data?: unknown, config?: RequestConfig): Promise<T> {
    return request.put<never, T>(url, data, config)
  },
  delete<T = unknown>(url: string, config?: RequestConfig): Promise<T> {
    return request.delete<never, T>(url, config)
  },
}

export function createRequestController() {
  return new AbortController()
}

declare module 'axios' {
  interface AxiosError {
    displayMsg?: string
  }
}
