import axios from 'axios';
import Vue from 'vue';

/**
 * 请求控制器
 * @type {Map<string, AbortController>}
 */
const requestController = new Map();

/**
 * 获取请求中断信号
 * @param key
 * @return {AbortSignal}
 */
export const getRequestSignal = (key) => {
  let controller = requestController.get(key);
  if (controller) controller.abort();
  requestController.set(key, controller = new AbortController());
  return controller.signal;
};

/**
 * 获取请求唯一 key
 * @param config 请求配置
 * @param [meta] 元数据
 * @return {string} key
 */
export const getRequestKey = (config, meta) => {
  if (!meta) meta = config.meta || {};
  return meta.key || config.url;
};

const http = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  withCredentials: true,
  timeout: 60 * 1000,
});

http.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) config.headers.Authorization = token;
  if (config['meta'] && config['meta'].share && !config.signal) {
    const key = getRequestKey(config);
    config.signal = getRequestSignal(key);
  }
  return config;
});

const magicCode = [-5, -9, -8];
http.interceptors.response.use(
    response => {
      const {meta = {}} = response.config;
      const res = response.data;
      // 设置 cookie
      if (response.headers['access-control-expose-headers']) {
        const cookie = response.headers['access-control-expose-headers'].split(';')[0];
        if (cookie) document.cookie = cookie;
      }
      // 直接返回响应数据
      if (meta['ignoreResponse']) return res;

      const {
        ResultCode = 0, ResultInfo, Data,
        code = ResultCode, message = ResultInfo, data = Data,
      } = res;
      if (code === 0) return {code, data, message};
      try {
        if (magicCode.includes(code)) {
          Vue.prototype.$router1.replace('/').then();
          throw message;
        } else if (code === 401) {
          const isAllow = sessionStorage.getItem('isAllow') || 'true';
          if (isAllow) Vue.prototype.$router1.replace('/').then();
          throw `登录过期,请重新登录`;
        } else {
          throw message || `错误，请重试！`;
        }
      } catch (error) {
        Vue.prototype.$message({
          message: error,
          type: 'error',
          showClose: true,
        });
      }
      return Promise.reject({
        message: message || `错误，请重试！`,
        ignoreError: meta['ignoreError'],
      });
    },
    error => {
      if (error.message !== 'canceled' && !error.ignoreError) Vue.prototype.$message({
        message: error.message,
        type: 'error',
      });
      return Promise.reject(error);
    },
);

export default http;