import Vue from "vue";
import moment from "moment";
import { reduce } from "lodash";
import jsEncrypt from "@/utils/encryptor";
import ApiConstant from "@/api/ApiConstant";
import LoginModalToast from "@/components/AppLogin/LoginModal";
import { generateTabName } from "@/plugins/generateRouter";
import { generateRandom } from "@/utils/util";
import { cancelRequestFilter } from "@/config/api";
import ErrorMessageToast from "@/components/ErrorMessageToast";

// 页面loading
let asyncApiCount = 0;

/**
 * 加载全局loading
 * @param count 是否显示loading Number
 *  1：显示 0：隐藏
 * @param i18n 国际化
 */
export function loadGlobalLoading(count, i18n) {
  const node = document.getElementById("app-loading");
  if (node) {
    count === 0 && node.remove();
  } else if (count === 1) {
    const text =
      typeof i18n?.t === "function"
        ? `<span>${i18n.t("GLOBAL_LOADING")}</span>`
        : "";
    const dom = document.createElement("div");
    dom.id = "app-loading";
    dom.onclick = (e) => {
      if (
        [
          e.target.className,
          e.target?.parentNode?.className,
          e.target?.parentNode?.parentNode?.className,
        ].includes("manually-close")
      ) {
        document.getElementById("app-loading").remove();
        asyncApiCount = 0;
      }
    };
    dom.innerHTML = `
      <div class="my-loading">
        <svg style="vertical-align: -0.2em;" viewBox="0 0 1024 1024" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="anticon-spin"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg>
        ${text}
        <span class="manually-close">
          <svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg>
        </span>
      </div>
    `;
    document.body.appendChild(dom);
  }
}

function addAsyncApiCount(config, i18n) {
  if (config.data?.isCancelApiLoad || config.params?.isCancelApiLoad) {
    return;
  }
  asyncApiCount += 1;
  loadGlobalLoading(asyncApiCount, i18n);
}

function removeAsyncApiCount(config) {
  if (config.data?.isCancelApiLoad || config.params?.isCancelApiLoad) {
    return;
  }
  asyncApiCount -= 1;
  loadGlobalLoading(asyncApiCount);
}

// 获取绑定在Vue的$message
const { $message } = Vue.prototype;
Vue.prototype.isMom = true;
const pendingRequest = new Map();

function generateReqKey(config) {
  const { method, url, params = {} } = config;
  const paramsStr = reduce(
    params,
    function (result, value, key) {
      const param = value ? `${key}=${value}` : "";
      return result + param;
    },
    ""
  );
  return [method, url, paramsStr].filter((item) => item).join();
}

function addPendingRequest(config, CancelToken) {
  if (cancelRequestFilter.some((e) => config.url.includes(e))) {
    return;
  }
  const requestKey = generateReqKey(config);
  // eslint-disable-next-line
  config.cancelToken =
    config.cancelToken ||
    new CancelToken((c) => {
      // An executor function receives a cancel function as a parameter
      if (!pendingRequest.has(requestKey)) {
        pendingRequest.set(requestKey, c);
      }
    });
}

function removePendingRequest(config) {
  const requestKey = generateReqKey(config);
  if (pendingRequest.has(requestKey)) {
    const cancelToken = pendingRequest.get(requestKey);
    cancelToken(requestKey);
    pendingRequest.delete(requestKey);
  }
}

const simpleErrorMessagePage = ["/login", "/reset"];

function errorModal(i18n, store, error, route, menuSuffix, locale) {
  if (
    simpleErrorMessagePage.findIndex((item) => route.path.startsWith(item)) > -1
  ) {
    $message.error({ content: error.msg, key: "Error" });
  } else {
    const meta = route.meta?.[0] || route.matched?.[0] || {};
    const businessName = generateTabName(menuSuffix, meta, i18n, locale);
    ErrorMessageToast({ i18n, store }, { error, businessName });
  }
}

export default (nuxtContext) => {
  const {
    $axios,
    store,
    app: { i18n: $i18n },
    redirect,
  } = nuxtContext;

  // 参考在线文档
  // 关于Nuxt中的$axios 的拦截器及其他方法
  // https://axios.nuxtjs.org/helpers

  // 定义 axios请求拦截器(request interceptor)
  $axios.onRequest((config) => {
    removePendingRequest(config); // 检查是否存在重复请求，若存在则取消已发的请求
    addPendingRequest(config, $axios.CancelToken); // 把当前请求信息添加到pendingRequest对象中
    addAsyncApiCount(config, $i18n);
    const timestamp = `${Date.now()}`;
    // nonce: 年月日时分秒毫秒+3位随机数+4位随机数+5位随机数
    const nonce =
      moment().format("YYYYMMDDHHmmssSSS") +
      generateRandom(100, 999) +
      generateRandom(1000, 9999) +
      generateRandom(10000, 99999);
    // eslint-disable-next-line no-param-reassign
    config.headers["Accept-Language"] = store.state?.app?.locale || "zh_CN"; // 语言
    // eslint-disable-next-line no-param-reassign
    config.headers.timestamp = timestamp; // 时间戳
    // eslint-disable-next-line no-param-reassign
    config.headers.nonce = nonce; // 临时请求码
    // eslint-disable-next-line no-param-reassign
    config.headers.signature = jsEncrypt.encrypt(timestamp + nonce); // 签名
    // eslint-disable-next-line no-param-reassign
    config.headers.site = store.state?.activatedSite || null; // 厂区
    // eslint-disable-next-line no-param-reassign
    config.headers.userName =
      encodeURIComponent(store?.state?.auth?.user?.user?.userName) || ""; // 用户名
    if (
      store.state?.app?.systemConfig.TENANT_OPEN === "true" &&
      store.state?.account?.tenantId &&
      store.state?.account?.tenantId !== undefined
    ) {
      // eslint-disable-next-line no-param-reassign
      config.headers.tenantId = store.state?.account?.tenantId; // 租戶id
    }
    return config;
  });

  // 定义axios 响应拦截器(response interceptor)
  $axios.onResponse((response) => {
    removePendingRequest(response.config); // 从pendingRequest对象中移除请求
    const { code = 200, success = true } = response?.data || {};
    // 状态码200，默认正常情况
    if (response.status === 200) {
      if (code === 200 && success) {
        removeAsyncApiCount(response.config);
        return response;
      }
    }
    const error = { response };
    return Promise.reject(error);
  });

  // 出错的统一处理
  $axios.onError((error) => {
    const { status, config, data = {} } = error?.response || {};
    if (config) {
      removePendingRequest(config); // 从pendingRequest对象中移除请求
      removeAsyncApiCount(config);
    } else if (error.__CANCEL__) {
      const [method, url] = (error.message ?? "").split(",");
      if (method && url) {
        const cg = { method, url };
        removePendingRequest(cg); // 从pendingRequest对象中移除请求
        removeAsyncApiCount(cg);
      }
    }
    const { route, $auth, app } = nuxtContext;
    const { systemConfig, locale } = store.state.app;

    if (config?.url?.includes("/api/sys/exception")) {
      return Promise.reject(error);
    }
    const info = {
      ...data,
      msg: data.msg || data.data || "Error",
    };

    if (status === 401) {
      // 服务器告知未授权
      if (info.code === 500) {
        errorModal(
          app.i18n,
          store,
          info,
          route,
          systemConfig.MENU_SUFFIX,
          locale
        );
        // eslint-disable-next-line
        return Promise.reject(error);
      }
      if (info.code === 401) {
        const isLogoutUrl =
          config.url === `${ApiConstant.PREFIX_CORE}/oauth/logout`;
        if (isLogoutUrl) {
          // eslint-disable-next-line
          return Promise.reject(error);
        }
        const isLoginPage =
          simpleErrorMessagePage.findIndex((item) =>
            route.path.startsWith(item)
          ) > -1;
        if ([isLoginPage, store.state.account.isInvalid].every((e) => !e)) {
          store.commit("account/SET_IS_INVALID", true);
          if (store.state.account.isReset) {
            store.commit("account/SET_IS_RESET", false);
            $auth.reset();
            redirect("/login");
          } else if (!window.$nuxt && !isLoginPage) {
            $auth.reset();
            redirect("/login");
          } else {
            LoginModalToast({
              store,
              i18n: $i18n,
            });
          }
        }
        // eslint-disable-next-line
        return Promise.reject(error);
      }
    } else if (status === 403) {
      // 服务器告知行为被拒绝
      errorModal(
        app.i18n,
        store,
        info,
        route,
        systemConfig.MENU_SUFFIX,
        locale
      );
      // eslint-disable-next-line
      return Promise.reject(error);
    } else if (status === 421) {
      redirect("/license");
    }
    if (!error.message) {
      errorModal(
        app.i18n,
        store,
        info,
        route,
        systemConfig.MENU_SUFFIX,
        locale
      );
    }
    // eslint-disable-next-line
    return Promise.reject(error);
  });

  const p = [
    $axios({
      url: `${ApiConstant.PREFIX_CORE}/api/sys/dict-data/DIC:CORE:I18N_LANGUAGE`,
      method: "GET",
      params: { enableFlag: "Y" },
    }),
  ];

  if (process.env.IS_PRODUCT_ENV === "1") {
    p.push(
      $axios({
        url: `${ApiConstant.PREFIX_CORE}/api/sys/i18n/list-modify`,
        headers: {
          Authorization: "",
        },
        method: "GET",
      })
    );
  } else {
    store.commit("app/SET_I18N_READY", true);
  }

  // 保留axios中header信息 国际化处理
  Promise.all(p).then((res) => {
    // 设置languages
    store.commit(
      "app/SET_LANGUAGES",
      res[0].data.data?.map((item) => ({
        label: item.label,
        value: item.value,
      }))
    );
    if (process.env.IS_PRODUCT_ENV === "1") {
      // 国际化信息获取成功
      store.commit("app/SET_I18N_READY", true);
      store.state.app.languages
        .map((item) => item.value)
        .forEach((lang) => {
          const reg = new RegExp(`_${lang}$`);
          $i18n.setLocaleMessage(lang, {
            // 本地国际化
            ...$i18n.messages[lang],
            // 接口国际化
            ...reduce(
              res[1].data.data,
              function (result, value, key) {
                if (reg.test(key)) {
                  // eslint-disable-next-line
                    result[key.replace(reg, "")] = value;
                }
                return result;
              },
              {}
            ),
          });
        });
    }
  });
};
