//  Copyright (c) 2024 Huawei Technologies Co., Ltd.
//  openUBMC is licensed under Mulan PSL v2.
//  You can use this software according to the terms and conditions of the Mulan PSL v2.
//  You may obtain a copy of Mulan PSL v2 at:
//        #  http://license.coscl.org.cn/MulanPSL2
//  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
//  EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
//  MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
//  See the Mulan PSL v2 for more details.
import { UI_REST_KEEPALIVE } from '@/api/api';
import { SecondAuthorityFail } from '@/model/base-enum';
import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { getStoreData, loading, setStoreData, showFailedMessage } from './composition';
import { getErrorTranslate } from './language';
import { escapeHeader, getMessageId } from './utils';

// 添加请求拦截
axios.interceptors.request.use((config: InternalAxiosRequestConfig) => {
  if (config?.headers) {
    config.headers.From = 'WebUI';
  }
  // 添加token
  if (config.method?.toLowerCase() !== 'get' && config.headers) {
    const loct = getStoreData('loct', 'to');
    config.headers['X-CSRF-Token'] = escapeHeader(loct);
  }
  return config;
});

// 创建请求资源池
let requestPool:any = [];

// 取消当前请求资源池中正在请求的接口
export function cancelRequests() {
  for (let item of requestPool) {
    item.cancel();
  }
  // 清空请求资源池
  requestPool = [];
}

export class IbmcHttp {
  public static errorCount: number = 0;
  public static timeStamp: number = 0;

  constructor() {}

  public keepAlive(mode: 'Activate' | 'Deactivate'): Promise<any> {
    /**
     * 如下几种情况下，不发keepAlive
     * 1. sessionId不存在（这种情况主要是在于退出后，在登录页或App里面发送保持会话的请求）。
     * 2. 已经发生了401超时错误
     * 3. 10秒内不连续发送
     */
    return new Promise((resolve, reject) => {
      const url = UI_REST_KEEPALIVE;
      const rn = getStoreData('loct', 'rn');
      if (!rn) {
        reject(new Error());
        return;
      }

      const nowDate = Date.now();
      if (mode === 'Activate') {
        if (
          !getStoreData('loct', 'rn') ||
          getStoreData('glob', 'isTimeOut') ||
          nowDate - IbmcHttp.timeStamp < 10 * 1000
        ) {
          reject(new Error());
          return;
        }
        IbmcHttp.timeStamp = nowDate;
      }

      axios.post(url, { Mode: mode }).then(
        res => {
          resolve(res);
        },
        error => {
          reject(error);
        },
      );
    }).catch(error => {});
  }

  public get(url: string, param?: any, config?: { keepAlive: boolean }, injectSource?: any): Promise<AxiosResponse> {
    return new Promise((resolve, reject) => {
      if (getStoreData('glob', 'isTimeOut')) {
        return;
      }

      if (config?.keepAlive) {
        this.keepAlive('Activate');
      }
      let source;
      if (injectSource) {
        source = injectSource;
      } else {
        source = axios.CancelToken.source();
      }
      requestPool.push(source);
      axios.get(url, {...param, cancelToken: source.token}).then(
        (res: AxiosResponse) => {
          this.resetErrorCount();
          resolve(res);
        },
        (error: AxiosError) => {
          this.errorHandle(error, 'get');
          this.rejectOperate(resolve, reject, error);
        },
      );
    });
  }

  public post(url: string, param?: any, config?: any): Promise<AxiosResponse> {
    return new Promise((resolve, reject) => {
      if (getStoreData('glob', 'isTimeOut')) {
        return;
      }

      if (config?.keepAlive) {
        this.keepAlive('Activate');
      }

      axios.post(url, param, config).then(
        (res: AxiosResponse) => {
          this.resetErrorCount();
          resolve(res);
        },
        (error: AxiosError) => {
          this.errorHandle(error, 'post', config);
          this.rejectOperate(resolve, reject, error);
        },
      );
    });
  }

  public patch(url: string, param?: any, config?: any): Promise<AxiosResponse> {
    return new Promise((resolve, reject) => {
      if (getStoreData('glob', 'isTimeOut')) {
        return;
      }

      if (config?.keepAlive) {
        this.keepAlive('Activate');
      }

      axios.patch(url, param, config).then(
        (res: AxiosResponse) => {
          this.resetErrorCount();
          resolve(res);
        },
        (error: AxiosError) => {
          this.errorHandle(error, 'patch', config);
          this.rejectOperate(resolve, reject, error);
        },
      );
    });
  }

  public put(url: string, param: any, config?: { keepAlive: boolean }): Promise<AxiosResponse> {
    return new Promise((resolve, reject) => {
      if (getStoreData('glob', 'isTimeOut')) {
        return;
      }

      if (config?.keepAlive) {
        this.keepAlive('Activate');
      }

      axios.put(url, param).then(
        (res: AxiosResponse) => {
          this.resetErrorCount();
          resolve(res);
        },
        (error: AxiosError) => {
          this.errorHandle(error, 'put', config);
          this.rejectOperate(resolve, reject, error);
        },
      );
    });
  }

  public delete(url: string, param?: any, config?: { keepAlive: boolean }): Promise<AxiosResponse> {
    return new Promise((resolve, reject) => {
      if (getStoreData('glob', 'isTimeOut')) {
        return;
      }

      if (config?.keepAlive) {
        this.keepAlive('Activate');
      }

      axios.delete(url, { data: param, ...config }).then(
        (res: AxiosResponse) => {
          this.resetErrorCount();
          resolve(res);
        },
        (error: AxiosError) => {
          this.errorHandle(error, 'delete', config);
          this.rejectOperate(resolve, reject, error);
        },
      );
    });
  }

  protected rejectOperate(
    resolve: ((value: AxiosResponse | PromiseLike<AxiosResponse>) => void),
    reject: (error?: AxiosResponse) => void,
    error: AxiosError,
  ): void {
    reject(error.response);
  }

  protected errorHandle(reqError: AxiosError, method?: string, config?: any): void {
    loading(false);
    const msg = reqError.message;
    if (msg === 'Network Error') {
      IbmcHttp.errorCount += 1;
    }

    const error = reqError.response;
    // 请求错误时，错误数加1
    if (error && error.status === 0) {
      IbmcHttp.errorCount += 1;
    }

    // 连续请求失败3次后，前端报超时，任意请求成功一次，该次数重置为0
    if (IbmcHttp.errorCount >= 3) {
      setStoreData('event', 'error401', `${getErrorTranslate('SessionTimeout')}${Date.now()}`);
      return;
    }

    if (typeof error !== 'object' || !error.data) {
      return;
    }

    this.reportError(error, method, config);
  }

  private resetErrorCount() {
    IbmcHttp.errorCount = 0;
  }

  private reportError(error: AxiosResponse<any>, method?: string, config?: any) {
    const errorId = getMessageId(error.data)[0]?.code;
    /**
     * 如果是二次认证、用户被锁定导致的401错误，则不处理
     * 正常401错误，触发超时提醒。
     * 其他类型的错误，使用错误处理来提示报错信息
     * 如果接口中带有ignoredErrorTip，则不使用错误处理服务来报错。
     */
    if (
      error.status === 401 &&
      (errorId === SecondAuthorityFail.AUTHORIZATIONFAILED ||
        errorId === SecondAuthorityFail.REAUTHFAILED ||
        errorId === SecondAuthorityFail.USERLOCKED)
    ) {
      return;
    }

    if (error.status === 401) {
      const errorMsg = errorId ? getErrorTranslate(errorId) : getErrorTranslate('SessionTimeout');
      setStoreData('event', 'error401', `${errorMsg}${Date.now()}`);
      return;
    }

    // get方法、或者请求参数里面带了 ignoredErrorTip 的，不需要提示默认操作失败的错误信息
    if (method === 'get' || config?.ignoredErrorTip) {
      return;
    }

    if (errorId) {
      // 提示操作失败
      const errorMsg = getErrorTranslate(errorId);
      showFailedMessage(errorMsg);
    }
  }
}

export default new IbmcHttp();
