import axios from 'axios'
import type { InternalAxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/stores/user'

// 1. 创建 axios 实例
const service = axios.create({
    // 【关键修改】
    // 设置为 '/api'，请求会自动拼接成 http://localhost:5173/api/...
    // 然后被 Vite 代理转发到 http://127.0.0.1:8000/api/...
    baseURL: '/api',
    timeout: 5000
})

// ============================================================
// 2. 防重复提交 - Pending 请求池
// ============================================================
const pendingRequests = new Map<string, AbortController>()

// 生成唯一请求 Key：方法 + URL + 序列化参数
const generateRequestKey = (config: InternalAxiosRequestConfig): string => {
    const method = (config.method || 'get').toLowerCase()
    const url = config.url || ''
    const params = config.params ? JSON.stringify(config.params) : ''
    const data = config.data ? JSON.stringify(config.data) : ''
    return `${method}:${url}:${params}:${data}`
}

// ============================================================
// 3. 无感刷新 Token 核心逻辑
// ============================================================

// 标记是否正在刷新 Token
let isRefreshing = false
// 重试队列：存储失败的请求，刷新成功后重新发送
const retryQueue: {
    resolve: (value: unknown) => void
    reject: (reason?: unknown) => void
    config: InternalAxiosRequestConfig
}[] = []

// 处理队列中的请求
const processQueue = (newToken: string | null, error?: Error) => {
    retryQueue.forEach(({ resolve, reject, config }) => {
        if (newToken) {
            // 更新 Authorization 头
            config.headers['Authorization'] = 'Bearer ' + newToken
            // 重新发送请求
            service(config).then(resolve).catch(reject)
        } else {
            reject(error)
        }
    })
    // 清空队列
    retryQueue.length = 0
}

// 刷新 Token 接口
const refreshToken = async (refreshTokenValue: string): Promise<string> => {
    const res = await axios.post('/api/v1/auth/refresh', {
        refresh_token: refreshTokenValue
    })
    const data = res.data
    if (data.access_token) {
        return data.access_token
    }
    throw new Error('Token 刷新失败')
}

// ============================================================
// 4. 请求拦截器（添加 Token + 防重复提交）
// ============================================================
service.interceptors.request.use(
    (config) => {
        // 1. 添加 Token
        const token = localStorage.getItem('access_token') || localStorage.getItem('token')
        if (token && config.headers) {
            // Flask-JWT-Extended 默认需要 'Bearer <token>' 格式
            config.headers['Authorization'] = 'Bearer ' + token
        }

        // 2. 防重复提交检查
        const requestKey = generateRequestKey(config)

        // 排除一些不需要防重复的请求（如查询类 GET 请求可以根据需求调整）
        const ignoreMethods = ['get', 'head']
        if (!ignoreMethods.includes((config.method || 'get').toLowerCase())) {
            if (pendingRequests.has(requestKey)) {
                // 取消之前的请求
                const controller = pendingRequests.get(requestKey)
                controller?.abort('正在处理中，请勿重复操作')
                pendingRequests.delete(requestKey)
                console.warn(`[防重复] 取消重复请求: ${requestKey}`)
            }

            // 创建新的 AbortController 并存储
            const controller = new AbortController()
            config.signal = controller.signal
            pendingRequests.set(requestKey, controller)
        }

        return config
    },
    (error) => {
        return Promise.reject(error)
    }
)

// ============================================================
// 5. 响应拦截器（核心：无感刷新 + 清理 pending）
// ============================================================
service.interceptors.response.use(
    (response) => {
        // 清理 pending 请求池
        const requestKey = generateRequestKey(response.config)
        if (pendingRequests.has(requestKey)) {
            pendingRequests.delete(requestKey)
        }

        // Axios 默认包了一层 data，所以这里取 response.data
        const res = response.data

        // 如果后端返回的是标准 Flask jsonify 结果，通常没有 code 字段（除非你自己封装了）
        // 如果你使用了标准 HTTP 状态码（200, 201等），Axios 会直接进入这里

        // 只有当业务逻辑明确返回错误码时才报错 (根据你的后端封装调整)
        if (res.code && res.code !== 200) {
            ElMessage.error(res.msg || 'Error')
            return Promise.reject(new Error(res.msg || 'Error'))
        } else {
            return res // 返回解包后的数据
        }
    },
    async (error: AxiosError) => {
        console.log('err: ' + error) // for debug

        // 清理 pending 请求池（无论成功还是失败都要清理）
        if (error.config) {
            const requestKey = generateRequestKey(error.config)
            if (pendingRequests.has(requestKey)) {
                pendingRequests.delete(requestKey)
            }
        }

        // 如果不是 axios 错误，直接抛出
        if (!error.response) {
            return Promise.reject(error)
        }

        const originalConfig = error.config as InternalAxiosRequestConfig & { _retry?: boolean }

        let message = error.message || '请求失败'

        // 处理 HTTP 状态码错误
        const isLoginEndpoint = error.config && error.config.url && error.config.url.includes('/login')
        const isRefreshEndpoint = error.config && error.config.url && error.config.url.includes('/refresh')

        const status = error.response.status
        const data = error.response.data as any

        // ============================================================
        // 核心：401 错误处理 + 无感刷新
        // ============================================================
        if (status === 401) {
            // 1. 如果是登录接口的 401，不执行刷新
            if (isLoginEndpoint) {
                message = data?.msg || '用户名或密码错误'
                ElMessage.error(message)
                return Promise.reject(error)
            }

            // 2. 如果是刷新接口的 401，说明 refresh_token 也过期了
            if (isRefreshEndpoint) {
                message = '登录已彻底过期，请重新登录'
                ElMessage.error(message)
                // 清空所有 Token，跳转登录页
                localStorage.clear()
                window.location.href = '/login'
                return Promise.reject(error)
            }

            // 3. 业务接口返回 401，尝试无感刷新
            if (!originalConfig) {
                return Promise.reject(error)
            }

            // 如果已经重试过（防止无限循环）
            if (originalConfig._retry) {
                message = '登录已过期，请重新登录'
                localStorage.clear()
                window.location.href = '/login'
                return Promise.reject(error)
            }

            // 标记为已重试
            originalConfig._retry = true

            // 获取 refresh_token
            const refreshTokenValue = localStorage.getItem('refresh_token')

            if (!refreshTokenValue) {
                // 没有 refresh_token，直接跳转登录
                message = '登录已过期，请重新登录'
                ElMessage.error(message)
                localStorage.clear()
                window.location.href = '/login'
                return Promise.reject(error)
            }

            // 4. 尝试刷新 Token
            if (!isRefreshing) {
                isRefreshing = true

                try {
                    console.log('正在刷新 Token...')
                    const newAccessToken = await refreshToken(refreshTokenValue)

                    // 刷新成功，更新本地 Token
                    const userStore = useUserStore()
                    userStore.setToken(newAccessToken)

                    // 更新当前请求的 Authorization
                    originalConfig.headers['Authorization'] = 'Bearer ' + newAccessToken

                    console.log('Token 刷新成功，重发队列中的请求')

                    // 处理队列中的请求
                    processQueue(newAccessToken)

                    // 重发当前请求
                    return service(originalConfig)

                } catch (refreshError) {
                    console.error('Token 刷新失败:', refreshError)

                    // 刷新失败，清空队列并跳转登录
                    processQueue(null, refreshError as Error)

                    message = '登录已彻底过期，请重新登录'
                    ElMessage.error(message)
                    localStorage.clear()
                    window.location.href = '/login'
                    return Promise.reject(refreshError)

                } finally {
                    isRefreshing = false
                }
            } else {
                // 5. 正在刷新中，将当前请求加入队列等待
                return new Promise((resolve, reject) => {
                    retryQueue.push({
                        resolve,
                        reject,
                        config: originalConfig
                    })
                })
            }

        } else if (status === 403) {
            message = '权限不足'
        } else if (status === 404) {
            message = '请求的资源不存在'
        } else if (status === 500) {
            message = '服务器内部错误'
        } else if (data && data.msg) {
            // 优先显示后端返回的错误信息
            message = data.msg
        }

        // 登录接口的错误由调用方单独处理，不再显示全局提示
        if (!isLoginEndpoint) {
            ElMessage.error(message)
        }
        return Promise.reject(error)
    }
)

export default service
