import {
  serverBaseUrl,
  serverTimeout
} from '@/common'
import axios, { AxiosRequestConfig } from 'axios'
import * as qs from 'qs'
import Toast from '@/components/toast'
import { useAppUserStore } from '@/common/app/app-user-store.ts'
import { throttle } from '@/common/utils/index.ts'

/**
 * HTTP 统一响应结构
 */
export interface R<T = any> {
  code: number;
  msg: string;
  message: any;
  data: T;
  headers?: AxiosRequestConfig['headers']
}

export enum ContentType {
  FORM = 'application/x-www-form-urlencoded',
  MLT_FORM = 'multipart/form-data',
  JSON = 'application/json',
  BINARY = 'application/octet-stream',
}


type AxiosConfig = Pick<AxiosRequestConfig, 'headers' | 'params' | 'responseType'>

// type ParamsSerializerType = Extract<AxiosRequestConfig['paramsSerializer'], any>;

/**
 * Query 参数处理器
 *
 * @param params 参数内容
 */
const paramsSerializer = (params: any) => {
  return qs.stringify(params, {indices: false, allowDots: true})
}

/**
 * 统一错误处理函数
 */
/* function errHandler(r?: R) {
 Toast.error(r?.message ?? '操作失败')
 } */
const errHandler = throttle(500, (r?: R) => {
  Toast.error(r?.message ?? '服务器错误')
})
/**
 * axios 实例
 */
const httpUtil = axios.create({
  timeout: serverTimeout,
  baseURL: serverBaseUrl,
  headers: {
    Accept: ContentType.JSON,
  },
})

/**
 * 配置请求拦截器
 */
httpUtil.interceptors.request.use(
    config => {
      const appUserStore = useAppUserStore()
      config.headers.Authorization = appUserStore.token
      config.formSerializer = {indexes: null}
      return config
    },
    error => {
      // TODO 请求失败日志
      console.error('HTTP 请求发送失败', error)
      return Promise.reject(error)
    },
)

/**
 * 配置响应拦截器
 */
httpUtil.interceptors.response.use(
    response => {
      // console.log('HTTP 请求结果', response.config.url, response)
      // vite 代理失败时 响应码为 200 响应内容为空
      if (response.config.responseType !== 'json') {
        return Promise.resolve(response)
      }
      if (response.data == null) {
        response.data = {code: 0, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers}
      }
      response.data.headers = response.headers
      if (response.data.code === 0) {
        return Promise.resolve(response)
      } else {
        return Promise.reject(response)
      }
    },
    error => {
      console.error('HTTP 请求失败', error)
      if (error.response != null) {
        error.response.data = {...error.response.data, headers: error.response.headers}
      } else if (error.request != null) {
        error.response = {
          data: {code: 9999, msg: '网络异常', message: '网络异常', data: null},
        }
      } else {
        error.response = {
          data: {code: 5555, msg: '请求发送失败', message: '请求发送失败', data: null},
        }
      }
      return Promise.reject(error.response.data)
    },
)

/**
 * GET 请求
 *
 * @param url 请求地址
 * @param params Query 参数
 * @param disposeErr 是否处理错误响应，默认-->true
 */
export function get<T = any>(url: string, params?: any, disposeErr: boolean = true) {
  return httpUtil.get<R<T>>(url, {params, paramsSerializer, responseType: 'json'})
      .then(({data}) => data)
      .catch(res => {
        if (disposeErr) errHandler(res)
        return Promise.reject(res)
      })
}

/**
 * POST 请求（通用）
 *
 * @param url 请求地址
 * @param body Body 参数
 * @param config Axios 配置
 * @param disposeErr 是否处理错误响应，默认-->true
 */
export function post<T>(url: string, body?: any, config?: AxiosConfig, disposeErr: boolean = true) {
  return httpUtil.post<R<T>>(url, body, {...config, responseType: 'json'})
      .then(({data}) => data)
      .catch(res => {
        if (disposeErr) errHandler(res)
        return Promise.reject(res)
      })
}

/**
 * POST 请求（编码表单）
 *
 * @param url 请求地址
 * @param body Body 参数
 * @param config Axios 配置
 * @param disposeErr 是否处理错误响应，默认-->true
 */
export function postForm<T>(url: string, body: any, config?: AxiosConfig, disposeErr: boolean = true) {
  return httpUtil.postForm<R<T>>(url, paramsSerializer(body),
      {
        headers: {
          ...(config?.headers ?? {}),
          'Content-Type': ContentType.FORM,
        },
        params: config?.params,
        responseType: config?.responseType ?? 'json',
      })
      .then(({data}) => data)
      .catch(res => {
        if (disposeErr) errHandler(res)
        return Promise.reject(res)
      })
}

/**
 * POST 请求（多部分表单）
 *
 * @param url 请求地址
 * @param body Body 参数
 * @param config Axios 配置
 * @param disposeErr 是否处理错误响应，默认-->true
 */
export function postMltForm<T>(url: string, body: any, config?: AxiosConfig, disposeErr: boolean = true) {
  return httpUtil.postForm<R<T>>(url, body,
      {
        headers: {
          ...(config?.headers ?? {}),
          'Content-Type': ContentType.MLT_FORM,
        },
        params: config?.params,
        responseType: config?.responseType ?? 'json',
      })
      .then(({data}) => data)
      .catch(res => {
        if (disposeErr) errHandler(res)
        return Promise.reject(res)
      })
}

function getFileName(contentDisposition: string) {
  // 检查content-disposition是否存在
  if (!contentDisposition) {
    return null
  }

  // 查找filename=部分
  const match = contentDisposition.match(/filename=(.+)/)
  if (!match || match.length < 2) {
    return null
  }

  // 提取并解码文件名
  const fileNameEncoded = match[1].trim()
  // 移除可能存在的引号
  const fileNameWithoutQuotes = fileNameEncoded.replace(/["']/g, '')
  // 解码URL编码的字符串
  return decodeURIComponent(fileNameWithoutQuotes)
}

export function download(url: string, params?: any, disposeErr: boolean = true) {
  return httpUtil.get(url, {params, paramsSerializer, responseType: 'arraybuffer'})
      .then(res => {
        let data = res.data
        if (!data || data.byteLength <= 0) {
          // 错误提示
          return
        }
        const contentDisposition = res.headers['Content-Disposition'] ?? res.headers['content-disposition']
        const fileName = getFileName(contentDisposition) ?? '下载的文件'
        // 将二进制流转为blob
        const blob = new Blob([ data ])
        // 创建新的URL并指向File对象或者Blob对象的地址
        const blobURL = window.URL.createObjectURL(blob)
        // 创建a标签，用于跳转至下载链接
        const tempLink = document.createElement('a')
        tempLink.style.display = 'none'
        tempLink.href = blobURL
        tempLink.setAttribute('download', decodeURI(fileName))
        // 兼容：某些浏览器不支持HTML5的download属性
        if (typeof tempLink.download === 'undefined') {
          tempLink.setAttribute('target', '_blank')
        }
        // 挂载a标签
        document.body.appendChild(tempLink)
        tempLink.click()
        document.body.removeChild(tempLink)
        // 释放blob URL地址
        window.URL.revokeObjectURL(blobURL)
      })
      .catch(res => {
        if (disposeErr) errHandler(res)
        return Promise.reject(res)
      })
}

export function getFileUrl(path: string) {
  const appUserStore = useAppUserStore()
  return serverBaseUrl + path + '?authorization=' + appUserStore.token
}

export default {
  get,
  post,
  postForm,
  postMltForm,
  download,
  getFileUrl,
}
