/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { HttpStatus } from '@nestjs/common';
import { FastifyReply, FastifyRequest } from 'fastify';
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { GatewayService } from '../gateway/gateway.service';
import { Environment } from '@/environment';
import { logger } from '../../utils';
import { CookieConstants } from '@/core/constants';

/**
 * 网络服务
 *
 * @author chitanda
 * @date 2021-08-11 12:08:31
 * @export
 * @class NetService
 */
export class NetService {
  /**
   * nacos地址计算服务
   *
   * @author chitanda
   * @date 2021-08-17 19:08:13
   * @private
   * @type {GatewayService}
   */
  private readonly nacosAddress: GatewayService = GatewayService.getInstance();
  /**
   * 服务名称
   *
   * @author chitanda
   * @date 2021-08-17 19:08:23
   * @private
   * @type {string}
   */
  private serviceName: string;

  /**
   * Creates an instance of NetService.
   *
   * @author chitanda
   * @date 2021-08-17 19:08:19
   * @param {string} [serviceTag] 服务标识，用于从配置文件中提取nacos服务名称
   */
  constructor(private serviceTag?: string) {}

  async get(
    request: FastifyRequest,
    response: FastifyReply,
    url: string,
    config?: AxiosRequestConfig,
  ): Promise<AxiosResponse<any>> {
    config = this.parameterProcessing(request, config);
    url = this.urlProcessing(url);
    this.printDebugLog('get', request, url);
    try {
      const res = await axios.get(url, config);
      return res;
    } catch (err) {
      this.errorProcessing(response, err);
    }
  }

  async delete(
    request: FastifyRequest,
    response: FastifyReply,
    url: string,
    config?: AxiosRequestConfig,
  ): Promise<AxiosResponse<any>> {
    config = this.parameterProcessing(request, config);
    url = this.urlProcessing(url);
    this.printDebugLog('delete', request, url);
    try {
      const res = await axios.delete(url, config);
      return res;
    } catch (err) {
      this.errorProcessing(response, err);
    }
  }

  async post(
    request: FastifyRequest,
    response: FastifyReply,
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<AxiosResponse<any>> {
    data = this.getData(request, data);
    config = this.parameterProcessing(request, config);
    url = this.urlProcessing(url);
    this.printDebugLog('post', request, url);
    try {
      const res = await axios.post(url, data, config);
      return res;
    } catch (err) {
      this.errorProcessing(response, err);
    }
  }

  async post2(
    request: FastifyRequest,
    response: FastifyReply,
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<AxiosResponse<any>> {
    data = this.getData(request, data);
    url = this.urlProcessing(url);
    this.printDebugLog('post2', request, url);
    try {
      const res = await axios.post(url, data, config);
      return res;
    } catch (err) {
      this.errorProcessing(response, err);
    }
  }

  async put(
    request: FastifyRequest,
    response: FastifyReply,
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<AxiosResponse<any>> {
    data = this.getData(request, data);
    config = this.parameterProcessing(request, config);
    url = this.urlProcessing(url);
    this.printDebugLog('put', request, url);
    try {
      const res = await axios.put(url, data, config);
      return res;
    } catch (err) {
      this.errorProcessing(response, err);
    }
  }

  async patch(
    request: FastifyRequest,
    response: FastifyReply,
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<AxiosResponse<any>> {
    data = this.getData(request, data);
    config = this.parameterProcessing(request, config);
    url = this.urlProcessing(url);
    this.printDebugLog('patch', request, url);
    try {
      const res = await axios.patch(url, data, config);
      return res;
    } catch (err) {
      this.errorProcessing(response, err);
    }
  }

  /**
   * 补充 Debug 模式下请求日志输出
   *
   * @author chitanda
   * @date 2022-03-28 14:03:01
   * @protected
   * @param {string} method
   * @param {FastifyRequest} request
   * @param {string} forwardAddress
   */
  protected printDebugLog(method: string, request: FastifyRequest, forwardAddress: string): void {
    logger.debug(method, `${request.url} => ${forwardAddress}`);
  }

  /**
   * 参数预处理，目前填充headers
   *
   * @author chitanda
   * @date 2021-08-15 17:08:33
   * @protected
   * @param {FastifyRequest} request
   * @param {AxiosRequestConfig} config
   * @return {*}  {*}
   */
  protected parameterProcessing(request: FastifyRequest, config: AxiosRequestConfig): any {
    if (!config) {
      config = {};
    }
    if (request) {
      config.headers = Object.assign(request.headers, config.headers || {});
    }
    return config;
  }

  /**
   * url处理
   *
   * @author chitanda
   * @date 2021-08-15 18:08:01
   * @protected
   * @param {string} url
   * @return {*}  {string}
   */
  protected urlProcessing(url: string): string {
    if (this.serviceTag) {
      if (!this.serviceName) {
        this.serviceName = this.nacosAddress.getServiceId(this.serviceTag);
      }
      const host = this.nacosAddress.getHost(this.serviceName);
      if (host) {
        return host + (!url.startsWith(Environment.BasePath) ? url : url.substring(Environment.BasePath.length));
      }
    }
    return url;
  }

  /**
   * 错误统一处理
   *
   * @author chitanda
   * @date 2022-03-28 14:03:43
   * @param {FastifyReply} response
   * @param {*} err
   */
  public errorProcessing(response: FastifyReply, err: any): void {
    if (err.response) {
      logger.error(`请求地址: ${err.config.url}`);
      logger.error(err.response.data);
      if (err.response.status === 401) {
        response.clearCookie(CookieConstants.USER_NAME);
      }
      response.code(err.response.status).send(err.response.data);
    } else {
      logger.error(err.message);
      response.code(HttpStatus.INTERNAL_SERVER_ERROR).send(err.message);
    }
  }

  /**
   * 计算请求数据
   *
   * @author chitanda
   * @date 2022-02-25 13:02:43
   * @protected
   * @param {FastifyRequest} req
   * @param {*} [data]
   * @return {*}  {*}
   */
  protected getData(req: FastifyRequest, data?: any): any {
    if (!data) {
      if (req.body) {
        data = req.body;
      } else {
        data = {};
      }
    }
    if (req.headers['Content-Length']) {
      delete req.headers['Content-Length'];
    }
    const buffer = Buffer.from(JSON.stringify(data));
    req.headers['content-length'] = buffer.length.toString();
    return data;
  }
}
