import axios from 'axios'
import fileDownload from 'js-file-download'
import { getStore, isExpired } from '@/utils/storage'
import { ElNotification } from 'element-plus'
import { encryptByMd5, uuid, sortByKey } from '@/utils/crypto'
import router from '@/router'
import qs from 'qs'

axios.defaults.headers.post['Content-Type'] = 'application/json'

const ajax = axios.create({
  baseURL: import.meta.env.VITE_APP_API_PREFIX,
  timeout: 100000
})

ajax.interceptors.request.use(
  (config) => {
    const isBasic = config.headers.isBasic || false
    if (!isBasic) {
      const expired = isExpired('accessToken')
      if (expired) {
        router.push({ name: 'login' })
      }
      config.headers.Authorization = 'Bearer ' + getStore('accessToken')
    }
    return config
  },
  (error) => Promise.reject(error)
)

ajax.interceptors.response.use(
  (res) => res,
  (error) => {
    if (!error.response) return Promise.reject(error)
    const data = error.response.data
    switch (data.code) {
      case 403:
        ElNotification({ title: '温馨提示', message: '您没有此权限', type: 'warning' })
        break
      case 400:
        ElNotification({ title: '温馨提示', message: data.msg, type: 'warning' })
        break
      case 500:
        ElNotification({ title: '温馨提示', message: '服务器错误。', type: 'error' })
        break
      case 401:
        ElNotification({
          title: data.msg,
          message: data.data,
          type: 'warning',
          onClose: () => router.push({ name: 'login', replace: true })
        })
        break
      default:
        return Promise.reject(error.response)
    }
    return Promise.reject(error.response)
  }
)

export interface FetchParams {
  url: string
  params?: Record<string, any>
  headers?: Record<string, any>
}

export interface ApiResponse<T = any> {
  code: number
  data: T
  msg: string
}

export function fetch<T = any>({ url = '', params = {}, headers = {} }: FetchParams): Promise<ApiResponse<T>> {
  headers['x-requestId'] = uuid()
  headers['x-timestamp'] = new Date().getTime()
  return new Promise((resolve, reject) => {
    ajax({
      method: 'GET',
      url,
      params,
      paramsSerializer: (p) => qs.stringify(p, { arrayFormat: 'indices' }),
      headers
    })
      .then((response) => {
        if (response.data && response.data.code === 200) resolve(response.data)
        throw response
      })
      .catch((error) => reject(error))
  })
}

export function post<T = any>({ url = '', params = {}, headers = {} }: FetchParams): Promise<ApiResponse<T>> {
  const requestId = uuid()
  const timestamp = new Date().getTime()
  const data = JSON.stringify(sortByKey(params))
  const sign = encryptByMd5(data + requestId + timestamp)
  headers['x-requestId'] = requestId
  headers['x-timestamp'] = timestamp
  headers['x-sign'] = sign
  return new Promise((resolve, reject) => {
    ajax({
      method: 'POST',
      url,
      data,
      headers
    })
      .then((response) => {
        if (response.data && response.data.code === 200) resolve(response.data)
        throw response
      })
      .catch((error) => reject(error))
  })
}

export function download({ url = '', params = {}, headers = {} }: FetchParams): Promise<void> {
  headers['x-requestId'] = uuid()
  headers['x-timestamp'] = new Date().getTime()
  headers['Content-Type'] = 'application/json'
  for (const propName of Object.keys(params)) {
    const value = params[propName]
    if (value instanceof Array && value.length > 0) params[propName] = value.toString()
  }
  return new Promise((resolve, reject) => {
    ajax({ method: 'GET', url, params, headers, responseType: 'arraybuffer' })
      .then((response) => {
        if (response.status === 200 && response.data) {
          const contentDisposition = response.headers['content-disposition']
          const fileName = contentDisposition
            ? contentDisposition.split('filename=')[1]
            : url.substring(url.lastIndexOf('/') + 1)
          fileDownload(response.data, decodeURI(fileName))
          resolve()
        }
        throw response
      })
      .catch((error) => reject(error))
  })
}

export function downloadPost({ url = '', params = {}, headers = {} }: FetchParams): Promise<void> {
  return new Promise((resolve, reject) => {
    ajax({ method: 'post', url, data: params, headers, responseType: 'arraybuffer' })
      .then((response) => {
        if (response.status === 200 && response.data) {
          const fileName = response.headers['content-disposition'].split('filename=')[1]
          fileDownload(response.data, decodeURI(fileName))
          resolve()
        }
        throw response
      })
      .catch((error) => reject(error))
  })
}
