import axios from "axios";
import { Message, MessageBox, Notification } from "element-ui";
import { objTypeOf } from "./utils";
import router from "../router";
import Vue from "vue";

import { getAuthorization } from "./crypto";
import { PLATFORM, DEVICE_ID } from "../constants/app";
import { initSyncTime, fetchTime } from "./syncServerTime";

import log4js from "./logger";
const logger = log4js.getLogger("request");

// logger
const addLog = (datas, type) => {
  if (type === "start") {
    const msg = `${datas.url}，开始请求`;
    logger.info(msg);
  } else if (type === "success") {
    const msg = `${datas.config.url}，请求成功`;
    logger.info(msg);
  } else {
    const msg = `${datas.config.url}，请求错误，错误信息：${datas.response.data.status} - ${datas.response.data.message}`;
    logger.error(msg);
  }
};

// axios interceptors
var load = "";
// 同一时间有多个请求时，会形成队列。在第一个请求创建loading，在最后一个响应关闭loading
var queue = [];
axios.interceptors.request.use(
  config => {
    // 显示loading提示
    if (!queue.length && !config["slientRequest"]) {
      load = Message({
        customClass: "el-message-loading",
        iconClass: "el-message__icon el-icon-loading",
        message: "加载中...",
        duration: 0
      });
    }

    queue.push(1);

    // 为请求头添加鉴权信息
    let token = Vue.ls.get("token");
    if (token) {
      const ids = {
        orgId: Vue.ls.get("orgId", ""),
        schoolId: Vue.ls.get("schoolId", ""),
        userId: Vue.ls.get("user", { id: "" }).id
      };
      Object.entries(ids).forEach(([key, val]) => {
        if (val === null || val === "null" || val === "") return;
        config.headers[key] = val;
      });

      // 新版鉴权
      const sessionId = Vue.ls.get("user", { sessionId: "" }).sessionId;
      const timestamp = fetchTime();
      const Authorization = getAuthorization(
        {
          token: token,
          timestamp,
          account: sessionId,
          uri: config.url.split("?")[0],
          method: config.method
        },
        "token"
      );
      config.headers["Authorization"] = Authorization;
      config.headers["time"] = timestamp;
    }
    // 为请求地址添加全局domain
    let domain = Vue.ls.get("domain", "");
    if (config.url.indexOf("http://") < 0) {
      config.url = domain + config.url;
    }
    config.headers["deviceId"] = DEVICE_ID;
    config.headers["platform"] = PLATFORM;
    config.headers["domain"] = domain;

    // 设置延迟时效
    config.timeout = 10 * 60 * 1000;

    addLog(config, "start");
    return config;
  },
  error => {
    // console.log(error);
    // logger.error("");
    // 关闭loading提示
    setTimeout(() => {
      queue.shift();
      if (!queue.length) load && load.close();
    }, 100);
    return Promise.reject(error);
  }
);
axios.interceptors.response.use(
  response => {
    initSyncTime(new Date(response.headers.date).getTime());
    addLog(response, "success");
    // 关闭loading提示
    setTimeout(() => {
      queue.shift();
      if (!queue.length) load && load.close();
    }, 100);
    return response;
  },
  error => {
    if (error.response) {
      initSyncTime(new Date(error.response.headers.date).getTime());
    }
    addLog(error, "error");

    // 关闭loading提示
    setTimeout(() => {
      queue.shift();
      if (!queue.length) load && load.close();
    }, 100);
    return Promise.reject(error);
  }
);

const mdData = datas => {
  let nData = {};
  if (!datas) return nData;
  Object.entries(datas).forEach(([key, val]) => {
    if (val === null || val === "null" || val === "") return;
    nData[key] = val;
  });
  return nData;
};

/**
 * errorCallback 请求失败的回调
 * @param {Object} error 请求失败时的错误信息
 */
const errorCallback = (error, config = {}) => {
  const slientRequest = config && config["slientRequest"];
  if (slientRequest) {
    return error;
  }

  if (error.response) {
    return errorDataCallback(error.response);
  }

  if (error.request) {
    let message = "请求错误";
    if (error.message.indexOf("timeout") > -1) {
      message = "请求超时";
    }
    Notification.error({ title: "错误提示", message });
    return error;
  }

  Notification.error({ title: "错误提示", message: "请求错误" });
  return error;
};

let unauthMsgBoxIsShow = false;
/**
 * errorDataCallback 请求成功，结果有误的回调
 * @param {Object} response Response信息
 */
const errorDataCallback = response => {
  if (objTypeOf(response.data) === "blob") return response.data;

  const error = response.data;
  let message = error.message || error.error || "请求错误";
  const unauthStatus = [401, 403];

  if (unauthStatus.includes(response.status)) {
    if (error.code === 401001 && message === "没有权限") {
      Notification.error({ title: "错误提示", message });
      return error;
    }

    if (unauthMsgBoxIsShow) return error;

    const exposeMsgs = ["系统授权信息已过期，请联系系统管理员激活!"];
    unauthMsgBoxIsShow = true;
    message = exposeMsgs.includes(message)
      ? message
      : "身份验证失效，请重新登录";
    MessageBox.confirm(message, "重新登陆?", {
      type: "warning",
      closeOnClickModal: false,
      closeOnPressEscape: false,
      showClose: false,
      callback: action => {
        unauthMsgBoxIsShow = false;
        if (action !== "confirm") return;
        router.push({ name: "Login" });
      }
    });
  } else {
    Notification.error({ title: "错误提示", message });
  }

  return error;
};

/**
 * response format
 *  {
      config, header, data, request, status, statusText
    }
 * 
 */

/**
 * successCallback 请求成功的回调
 * @param {Object} data Response中的data信息
 */
const successCallback = data => {
  if (data.code === 200) {
    if (
      objTypeOf(data.data) === "object" &&
      Object.prototype.hasOwnProperty.call(data.data, "success")
    ) {
      if (data.data.success) {
        return data.data;
      } else {
        return Promise.reject(data.data);
      }
    } else {
      return data.data;
    }
  } else {
    return Promise.reject(data.data);
  }
};

/**
 * get请求
 * @param {String} url 请求地址
 * @param {Object} datas 请求数据
 */
const $get = (url, datas) => {
  return axios
    .get(url, { params: mdData(datas) })
    .then(rep => {
      return successCallback(rep.data);
    })
    .catch(error => {
      return Promise.reject(errorCallback(error));
    });
};

/**
 * get请求
 * @param {String} url 请求地址
 * @param {Object} datas 请求数据
 */
const $postParam = (url, datas, config = {}) => {
  return axios
    .post(url, {}, { ...config, params: mdData(datas) })
    .then(rep => {
      if (config["responseType"] === "blob") return rep;

      return successCallback(rep.data);
    })
    .catch(error => {
      return Promise.reject(errorCallback(error, config));
    });
};

/**
 * post请求
 * @param {String} url 请求地址
 * @param {Object} datas 请求数据
 */
const $post = (url, datas, config = {}) => {
  let sqDatas = {};
  if (datas.constructor === Object) {
    sqDatas = mdData(datas);
  } else {
    sqDatas = datas;
  }

  return axios
    .post(url, sqDatas, config)
    .then(rep => {
      if (config["responseType"] === "blob") return rep;

      return successCallback(rep.data);
    })
    .catch(error => {
      return Promise.reject(errorCallback(error, config));
    });
};

/**
 * delete请求
 * @param {String} url
 * @param {Object} datas
 */
const $del = (url, datas) => {
  return axios
    .delete(url, { params: mdData(datas) })
    .then(rep => {
      return rep.data;
    })
    .catch(error => {
      return Promise.reject(errorCallback(error));
    });
};

/**
 * put 请求
 * @param {String} url 请求地址
 * @param {Object} datas 请求数据
 */
const $put = (url, datas) => {
  return axios
    .put(url, { params: mdData(datas) })
    .then(rep => {
      return rep.data;
    })
    .catch(error => {
      return Promise.reject(errorCallback(error));
    });
};

/**
 * patch请求
 * @param {String} url 请求地址
 * @param {Object} datas 请求数据
 */
const $patch = (url, datas) => {
  return axios
    .patch(url, { params: mdData(datas) })
    .then(rep => {
      return rep.data;
    })
    .catch(error => {
      return Promise.reject(errorCallback(error));
    });
};

export { $get, $postParam, $post, $del, $put, $patch };
