/**
 * 该文件可自行根据业务逻辑进行调整
 */
import type { AxiosResponse, CreateAxiosOptions, CreateInternalAxiosRequestConfig, RequestInterceptorConfig, RequestOptions } from '@vben/request';

import { AesEncryption, isObject } from '@jnpf/utils';

import { useAppConfig } from '@vben/hooks';
import { $t } from '@vben/locales';
import { preferences } from '@vben/preferences';
import { errorMessageResponseInterceptor, RequestClient, ResultEnum } from '@vben/request';
import { useAccessStore } from '@vben/stores';

import { message } from 'ant-design-vue';

import { router } from '#/router';
import { useAuthStore } from '#/store';
import { getJnpfAppEnCode, getRealJnpfAppEnCode } from '#/utils/jnpf';

const aesEncryption = new AesEncryption();
const { apiURL, reportApiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);

// 请求之前处理config
function beforeRequestHook(): RequestInterceptorConfig {
  return {
    fulfilled(config) {
      // 数据加密
      if (config?.requestOptions?.useCipher && config.data) {
        const resultStr = aesEncryption.encryptByAES(JSON.stringify(config.data));
        if (resultStr) config.data = { encryptData: resultStr };
      }
      return config;
    },
  };
}
// 数据处理，方便区分多种处理方式
function transformResponseHook() {
  return {
    fulfilled(res: AxiosResponse<any>) {
      const options = (res.config as CreateInternalAxiosRequestConfig)?.requestOptions;
      // 数据解密
      if ((options as RequestOptions).useCipher) {
        const resultStr = aesEncryption.decryptByAES(res.data);
        if (resultStr) res.data = JSON.parse(resultStr);
      }
      const { isTransformResponse, isReturnNativeResponse } = options as RequestOptions;
      // 是否返回原生响应头 比如：需要获取响应头时使用该属性
      if (isReturnNativeResponse) {
        return res;
      }
      // 不进行任何处理，直接返回
      // 用于页面代码可能需要直接获取code,data,msg这些信息时开启
      if (!isTransformResponse) {
        return res.data;
      }
      if (!res.data) {
        throw new Error($t('fallback.http.internalServerError'));
      }
      //  这里 code,data,msg为 后台统一的字段，需要在 types.ts内修改为项目自己的接口返回格式
      const { code, msg } = res.data;

      // 这里逻辑可以根据项目进行修改
      const hasSuccess = res.data && isObject(res.data) && Reflect.has(res.data, 'code') && code === ResultEnum.SUCCESS;
      if (hasSuccess) {
        return res.data;
      }
      // 在此处根据自己项目的实际情况对不同的code执行不同的操作
      // 如果不希望中断当前请求，请return数据，否则直接抛出异常即可
      let errorMsg = '';
      switch (code) {
        case ResultEnum.FORBIDDEN: {
          errorMsg = msg || $t('fallback.http.forbidden');
          if (getRealJnpfAppEnCode() && res.config.url?.includes('oauth/CurrentUser')) {
            const authStore = useAuthStore();
            authStore.goForbidden();
          }
          break;
        }
        case ResultEnum.TOKEN_ERROR:
        case ResultEnum.TOKEN_LOGGED:
        case ResultEnum.TOKEN_TIMEOUT: {
          errorMsg = msg || $t('fallback.http.internalServerError');
          const accessStore = useAccessStore();
          const authStore = useAuthStore();
          accessStore.setAccessToken(null);
          authStore.logout();
          break;
        }
        default: {
          errorMsg = msg || $t('fallback.http.internalServerError');
        }
      }
      if ((options as RequestOptions).errorMessageMode === 'message') {
        message.error(errorMsg);
      }
      throw new Error(errorMsg);
    },
  };
}

function createRequestClient(opt?: Partial<CreateAxiosOptions>) {
  const client = new RequestClient(opt || {});

  // 请求头处理
  client.addRequestInterceptor({
    fulfilled: async (config) => {
      const accessStore = useAccessStore();

      if (accessStore.accessToken) config.headers.Authorization = accessStore.accessToken;
      const routeAppCode = getJnpfAppEnCode();
      const realAppCode = getRealJnpfAppEnCode();
      let appScope = 'CONSOLE';
      if (routeAppCode.toLowerCase().startsWith('backend_')) {
        appScope = 'BACKEND';
      } else if (routeAppCode) {
        appScope = 'FRONTEND';
      }
      config.headers['App-Code'] = realAppCode;
      config.headers['Jnpf-App-Scope'] = appScope;
      config.headers['Accept-Language'] = preferences.app.locale;
      config.headers['Jnpf-Origin'] = 'pc';
      config.headers['Vue-Version'] = '3';
      // 审计「操作入口」：用户是从哪个菜单点进来的。route.meta.modelId 即 base_module.f_id
      // （见 utils/generate-routes-backend.ts 的 modelId: node.id）。
      // 放在全局拦截器而不是各业务接口各传一次：表单保存、列表按钮、流程动作三类场景一次覆盖。
      // 用 router 实例而不是 useRouter()——后者是组合式 API，只能在 setup 上下文里调。
      const menuId = router.currentRoute.value?.meta?.modelId;
      if (menuId) config.headers['Jnpf-Menu-Id'] = String(menuId);
      return config;
    },
  });

  client.addRequestInterceptor(beforeRequestHook());

  // response数据解构
  client.addResponseInterceptor({
    fulfilled: (response) => {
      const { status } = response;

      if (status >= 200 && status < 400) {
        return response;
      }
      throw Object.assign({}, response, { response });
    },
  });

  // 通用的错误处理,如果没有进入上面的错误处理逻辑，就会进入这里
  client.addResponseInterceptor(
    errorMessageResponseInterceptor((msg: string, error) => {
      // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理，根据不同的 code 做不同的提示，而不是直接使用 message.error 提示 msg
      // 当前mock接口返回的错误字段是 error 或者 message
      const responseData = error?.response?.data ?? {};
      const errorMessage = responseData?.error ?? responseData?.msg ?? '';
      // 如果没有错误信息，则会根据状态码进行提示
      message.error(errorMessage || msg);
    }),
  );

  client.addResponseInterceptor(transformResponseHook());

  return client;
}

export const defHttp = createRequestClient({ requestOptions: { apiUrl: apiURL } });

// 报表接口
export const reportHttp = createRequestClient({ requestOptions: { apiUrl: reportApiURL } });
