/**
 * 该文件可自行根据业务逻辑进行调整
 */
import type { RequestClientOptions } from '@vben/request';

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

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

import { useAuthStore } from '#/store';

import { refreshTokenApi } from './core';

const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);

function createRequestClient(baseURL: string, options?: RequestClientOptions) {
  const client = new RequestClient({
    ...options,
    baseURL,
  });

  /**
   * 重新认证逻辑
   */
  async function doReAuthenticate() {
    console.warn('Access token or refresh token is invalid or expired. ');
    const accessStore = useAccessStore();
    const authStore = useAuthStore();
    accessStore.setAccessToken(null);

    if (preferences.app.loginExpiredMode === 'modal' && accessStore.isAccessChecked) {
      accessStore.setLoginExpired(true);
    } else {
      // 短暂延迟后跳转，让用户看到提示
      setTimeout(() => {
        authStore.logout();
      }, 1000);
    }
  }

  /**
   * 刷新token逻辑
   */
  async function doRefreshToken() {
    const accessStore = useAccessStore();
    const resp = await refreshTokenApi();
    const newToken = resp.data;
    accessStore.setAccessToken(newToken);
    return newToken;
  }

  function formatToken(token: null | string) {
    return token ? `${token}` : null;
  }

  // 处理请求参数中的undefined值，将其替换为空字符串
  function handleUndefinedParams(params: any): any {
    if (!params || typeof params !== 'object') {
      return params;
    }

    const handleUndefined = (obj: any): any => {
      if (Array.isArray(obj)) {
        return obj.map((item) => handleUndefined(item));
      }

      if (obj !== null && typeof obj === 'object') {
        const result: any = {};
        for (const key in obj) {
          if (Object.prototype.hasOwnProperty.call(obj, key)) {
            const value = obj[key];
            if (value === undefined) {
              result[key] = '';
            } else if (value !== null && typeof value === 'object') {
              result[key] = handleUndefined(value);
            } else {
              result[key] = value;
            }
          }
        }
        return result;
      }

      return obj;
    };

    return handleUndefined(params);
  }

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

      config.headers.token = formatToken(accessStore.accessToken);
      config.headers['Accept-Language'] = preferences.app.locale;

      // 处理请求参数中的undefined值
      if (config.params) {
        config.params = handleUndefinedParams(config.params);
      }

      // 处理请求体中的undefined值
      if (config.data && !(config.data instanceof FormData)) {
        // 如果是FormData，不进行处理，避免丢失参数
        // FormData不需要处理undefined值，因为浏览器会自动处理
        config.data = handleUndefinedParams(config.data);
      }

      return config;
    },
  });

  // 自定义拦截器，处理后端返回的status为200但code不为0的情况
  client.addResponseInterceptor({
    fulfilled: (response) => {
      const { data: responseData, status } = response;

      // 检查HTTP状态码为200但响应数据中code不为0的情况
      if (status === 200 && responseData?.code !== 0) {
        // 创建一个错误对象，模拟HTTP错误响应
        const error = new Error(`Request failed with code ${responseData.code}`);
        Object.assign(error, {
          response: {
            ...response,
            status: responseData.code, // 将后端返回的code作为HTTP状态码
            data: responseData,
          },
        });

        // 对于特定的错误码，执行特殊处理
        if (responseData.code === 401) {
          // 登录失效的情况，触发重新认证逻辑
          // 不要等待doReAuthenticate完成，因为它会跳转页面，不需要等待
          doReAuthenticate().catch((error_) => {
            console.error('doReAuthenticate error:', error_);
          });
          // 对于401错误，不抛出错误，而是返回一个特殊标记的响应
          // 这样可以避免控制台显示未捕获的错误
          return {
            ...response,
            __isLoginExpired: true,
          } as any;
        }

        // 抛出错误，让后续的错误处理拦截器处理
        throw error;
      }

      // 对于其他情况，正常返回响应
      return response;
    },
  });

  // 处理返回的响应数据格式
  client.addResponseInterceptor(
    defaultResponseInterceptor({
      codeField: 'code',
      dataField: 'data',
      successCode: 0,
    }),
  );

  // token过期的处理
  client.addResponseInterceptor(
    authenticateResponseInterceptor({
      client,
      doReAuthenticate,
      doRefreshToken,
      enableRefreshToken: preferences.app.enableRefreshToken,
      formatToken,
    }),
  );

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

      // 如果是401错误，已经在自定义拦截器中处理了，这里不需要额外提示
      if (responseCode === 401) {
        return Promise.reject($t('ui.fallback.http.unauthorized'));
      }

      // 如果有后端返回的错误信息，优先显示
      if (errorMessage) {
        message.error(errorMessage);
      } else if (msg) {
        // 否则显示默认的错误信息
        message.error(msg);
      }
    }),
  );

  return client;
}

export const requestClient = createRequestClient(apiURL, {
  responseReturn: 'data',
  timeout: 0, // 设置为0表示没有超时时间限制
});

export const baseRequestClient = new RequestClient({
  baseURL: apiURL,
  timeout: 0, // 设置为0表示没有超时时间限制
});
