import axios, { AxiosRequestConfig, Method } from 'axios'
import cryptojs from 'crypto-js'
import service from '@/services'
import eventBus from '@/services/bus'
import { encryptAES, encryptRSA, generateRandomString } from '@/utils/crypto'
import { useLoginStore } from '@/stores'
import { localData } from '@/stores/storage'
import { CommonFetchOptions, CommonResult, FetchTypes, ResultCode } from './types'
//import { addPending, removePending } from './pending'

const loginStore = useLoginStore()

export default new (class {
    private readonly axiosInstance = axios.create({
        timeout: 30000,
    })

    constructor() {
        // 请求拦截器
        this.axiosInstance.interceptors.request.use(
            (config) => {
                //addPending(config) //将当前请求添加到列表中
                return config
            },
            (err) => {
                console.error(err)
                return Promise.reject('发生错误，请稍后再试')
            }
        )

        // 响应拦截器
        this.axiosInstance.interceptors.response.use(
            (res) => {
                //removePending(res) //在请求结束后，移除本次请求
                const { authorization } = res.headers
                if (authorization) {
                    loginStore.setToken(authorization) // 更新 Token
                }
                return res
            },
            (err) => {
                if (err.message === 'Network Error') {
                    return Promise.reject('网络或服务器错误')
                }
                if (err.response) {
                    const { code, msg, message } = err.response.data ?? {}
                    switch (err.response.status) {
                        case 408: {
                            return Promise.reject('请求超时，请稍后再试')
                        }
                        default: {
                            if (loginStore.token && code === ResultCode.InvalidToken) {
                                eventBus.$emit('Logout', '登录失效，请重新登录')
                            }
                            return Promise.reject(msg || message)
                        }
                    }
                }
                return Promise.reject('发生错误，请稍后再试')
            }
        )
    }

    private requestId = 0

    /** 
     * 请求暂停状态
     * 建议在过渡动画中启用，动画结束时禁用
     * 可防止在动画过程中同时请求数据，数据请求先于动画完成，双绑数据的页面会实时渲染，导致动画卡顿的问题
     */
    private paused = false

    // 响应队列
    private responseMap: Map<number, () => void> = new Map()

    /** 
     * 设置暂停状态
     * 在动画开始时设置一个 paused 状态，在动画结束后取消 paused 状态
     */
    setPause(isPaused: boolean) {
        this.paused = isPaused
        // 当 paused 状态为 false 时返回所有响应函数
        if (!isPaused) {
            this.responseMap.forEach((resolve) => resolve())
            this.responseMap.clear()
        }
    }

    /**
     * 签名和加密
     * @param options 
     * @returns 
     */
    private fetchAuth(config: AxiosRequestConfig) {
        const timestamp = new Date().getTime()

        //设置请求头
        config.headers = {
            'Accept-Language': localData.getValue('appLanguage'),
            'Sign-Id': loginStore.signId,
            Sign: cryptojs.SHA256(loginStore.sign + timestamp.toString()).toString(),
            Authorization: loginStore.token,
            Timestamp: timestamp.toString(),
            ...config.headers
        }

        if (config.data && config.method?.toLowerCase() === 'post') {
            console.log(config.data)
            const aesKey = generateRandomString(32) // 生成随机密钥
            const serializedData = JSON.stringify(config.data) // 数据序列化
            const encryptedData = encryptAES(serializedData, aesKey) // 数据加密

            config.headers['Content-Type'] = 'text/plain;charset=UTF-8'
            config.headers['Data-Key'] = encryptRSA(aesKey, loginStore.rsakey) // 使用公钥对随机密钥进行加密
            config.data = encryptedData
        }

        return config
    }

    /**
     * Http 请求
     * @param config 
     * @param requestId 
     * @returns 
     */
    request<T>(config: AxiosRequestConfig, requestId?: number) {
        const id = requestId ?? this.requestId++
        return new Promise<T>((resolve, reject) => {
            this.axiosInstance(config).then((res) => {
                // 待优化
                if (this.paused) {
                    this.responseMap.set(id, () => resolve(res.data))
                } else {
                    resolve(res.data)
                }
            }).catch((error) => {
                const message = error ?? (error ? '请求失败: ' + error : '请求失败，请稍后重试')
                reject(message)
            })
        })
    }

    /**
     * Http 通用请求
     * @param url 
     * @param method 
     * @param options 
     * @returns 
     */
    async commonRequest<T extends FetchTypes>(url: string, method: Method = 'get', options: CommonFetchOptions<T> = {}) {
        const controller = options.controller ?? new AbortController()
        const baseUrl = service.getConfig('apiUrl')

        const config: AxiosRequestConfig = {
            method,
            url: baseUrl + url,
            headers: options.headers,
            signal: controller.signal
        }

        if (method.toLowerCase() === 'get') {
            config.params = options.data
        } else {
            config.data = options.data
        }

        const res = await this.request<CommonResult<T['response']>>(this.fetchAuth(config))

        switch (res.code) {
            case ResultCode.InvalidToken:
                return Promise.reject('令牌无效')
            case ResultCode.Success:
                return res
            default:
                return Promise.reject(res.message)
        }
    }

    /**
     * 文件流请求
     * @param config 
     * @returns 
     */
    blobRequest(config: AxiosRequestConfig) {
        const baseUrl = service.getConfig('apiUrl')
        config.url = baseUrl + config.url
        config.responseType = 'blob'

        return this.axiosInstance(this.fetchAuth(config))
    }
})