/*
 * @Description: 请求公共类，不包含任何业务逻辑
 * @Date: 2021-01-25 16:44:20
 * @LastEditors: gzw
 * @LastEditTime: 2021-10-12 19:34:33
 */
import strategyModes from "@/config/http.config";
import { encryption } from "@/utils/encrypt";
import store from "@/store";
const ERR_MESSAGE_MAP = {
  400: "错误请求",
  401: "您未登录或登录超时，请重新登录",
  403: "拒绝访问",
  404: "请求错误,未找到该资源",
  405: "请求方法未允许",
  408: "请求超时",
  500: "服务器端出错",
  501: "网络未实现",
  502: "网络错误",
  503: "服务不可用",
  504: "网络超时",
  505: "http版本不支持该请求"
};
const defaultStratege = {
  default: {
    request(cfg) {
      return cfg;
    },
    response(res) {
      return res.data;
    }
  }
};

const config = {
  timeout: 15000,
  dataType: "json"
};
let isShowLoading = false;
let reqNum = 0;
let timeId = null;
let requestTree = {};
strategyModes.default = defaultStratege;

function beforeRequest() {
  reqNum++;
  clearTimeout(timeId);
  timeId = setTimeout(() => {
    uni.showLoading({
      /* #ifdef MP-WEIXIN */
      mask: true,
      /* #endif */
      title: "加载中",
      success: () => {
        isShowLoading = true;
      }
    });
  }, 1300);
}

function afterRequest() {
  reqNum--;
  if (reqNum <= 0) {
    clearRequest();
    if (isShowLoading) {
      uni.hideLoading();
      isShowLoading = false;
    }
  }
}

function clearRequest() {
  clearTimeout(timeId);
}

function repeatUrl(url) {
  if (process.env.NODE_ENV === "production" || url.indexOf("holmes.liangkebang.com") > -1)
    return url;
  const { isNotReleaseEnv, currentEnv } = store.state.qa;
  if (isNotReleaseEnv && currentEnv) {
    return url.replace(/[^\-)]*?(?=\.)/, currentEnv);
  }
  return url;
}

/**
 * @description: uni.request封装，使其支持请求策略
 * @param {String} url 地址
 * @param {Object} options 请求配置，包括：参数-data、请求头-header、request配置
 * @param {Boolean} log 是否
 * @return {Promise} reslove 返回数组
 */
function requestFrame(url = "", options = {}, log = true) {
  return new Promise(resolve => {
    !options.hideLoading && beforeRequest(); // 隐藏loading
    // 默认策略
    if (!options.strategy) {
      options.strategy = "service";
    }
    // 处理请求数据
    if (strategyModes[options.strategy].request) {
      options = strategyModes[options.strategy].request(options);
    }
    // 加密数据
    if (options.encrypt) {
      options.data = encryption(options.data);
    }
    // 处理接口上报设备信息
    if (options.reportDeviceId) {
      if (!options.data) {
        options.data = {
          shumeiDeviceId: uni.getStorageSync("smDeviceId") || ""
        };
      } else {
        options.data.shumeiDeviceId = uni.getStorageSync("smDeviceId") || "";
      }
    }
    options = { ...config, ...options };
    const { timeout, dataType, responseType, withCredentials, method, data, header } = options;
    const reqUrl = repeatUrl(url);
    requestTree[reqUrl] = uni.request({
      url: reqUrl,
      timeout,
      dataType,
      // #ifndef MP-ALIPAY
      responseType,
      // #endif
      withCredentials,
      method,
      data,
      header,
      success: res => {
        // log && console.log(res.data);
        !options.hideLoading && afterRequest();
        // 处理response
        resolve(strategyModes[options.strategy].response({ res, options }));
      },
      fail: err => {
        !options.hideLoading && afterRequest();

        let message = "";

        if (err.message === "Network Error" && !err.response) {
          message = "服务不可用";
        } else if (err.response?.status) {
          message = ERR_MESSAGE_MAP[err.response.status] || `未知异常码: ${err.response.status}`;
        }

        if (err.errMsg !== "request:fail abort") {
          uni.showToast({
            title: message || "服务异常，请稍后重试",
            icon: "none",
            duration: 2500
          });
        }

        const error = new Error(message);
        log && console.log(err);
        resolve([null, error]);
      }
    });
  });
}

export default {
  get(url, data, options, log) {
    return requestFrame(url, { method: "GET", data, ...options }, log);
  },
  post(url, data, options, log) {
    return requestFrame(url, { method: "POST", data, ...options }, log);
  },
  abortAll() {
    Object.keys(requestTree).forEach(key => {
      requestTree[key].abort();
    });
  },
  request: requestFrame
};
