import { Environment } from '@/environment';
import { HttpStatus } from '@nestjs/common';
import { AxiosRequestConfig, AxiosResponse } from 'axios';
import { FastifyRequest, FastifyReply } from 'fastify';
import {  notNilEmpty } from 'qx-util';
import { NetService } from '../../service';

/**
 * 控制器基类
 *
 * @author chitanda
 * @date 2021-08-25 16:08:37
 * @export
 * @abstract
 * @class ControllerBase
 */
export abstract class ControllerBase {
  /**
   * 请求服务
   *
   * @author chitanda
   * @date 2021-08-25 16:08:32
   * @protected
   * @abstract
   * @type {NetService}
   */
  protected abstract readonly http: NetService;

  /**
   * 当前可替换的 path 路径
   *
   * @author chitanda
   * @date 2021-10-19 15:10:43
   * @protected
   * @type {RegExp}
   */
  protected readonly pathReplaceReg: RegExp = null;

  /**
   * 路径对应表
   *
   * @author chitanda
   * @date 2021-10-19 15:10:35
   * @protected
   * @type {Map<string, string>}
   */
  protected readonly pathMap: Map<string, string> = new Map();

  /**
   * Creates an instance of ControllerBase.
   *
   * @author chitanda
   * @date 2021-10-19 15:10:05
   */
  constructor() {
    this.init();
  }

  /**
   * 初始化
   *
   * @author chitanda
   * @date 2021-10-19 15:10:19
   * @protected
   */
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  protected init(): void { }

  /**
   * 编译 URL
   *
   * @author chitanda
   * @date 2021-10-19 16:10:15
   * @protected
   * @param {string} url
   * @return {*}  {string}
   */
  protected parseUrl(url: string): string {
    if (this.pathReplaceReg != null) {
      if (this.pathReplaceReg.test(url)) {
        if (url.indexOf(Environment.BasePath) === 0) {
          url = url.replace(Environment.BasePath, '');
        }
        const strList: string[] = url.split('/').splice(1);
        for (let i = 0; i < strList.length; i++) {
          const path = strList[i];
          if (i % 2 === 0) {
            if (this.pathMap.has(path)) {
              strList[i] = this.pathMap.get(path);
            }
          }
        }
        return '/' + strList.join('/');
      }
    }
    return url;
  }

  /**
   * 请求服务API
   *
   * @author chitanda
   * @date 2021-08-25 16:08:54
   * @protected
   * @param {FastifyRequest} req
   * @param {string} url
   * @param {*} [data]
   * @param {AxiosRequestConfig} [config]
   */
  protected async callAPI(
    req: FastifyRequest,
    res: FastifyReply,
    url: string,
    data?: any,
    config: AxiosRequestConfig = {},
  ): Promise<void> {
    if (!config.headers) {
      config.headers = {};
    }
    url = this.transformationPrams(url, req.query);
    Object.assign(config.headers, { 'Content-Type': 'application/json' });
    const result = await this.http.post(req, res, Environment.DefaultAPI.Path + url, data, config);
    if (result) {
      this.parseResponse(req, res, result);
    }
  }

  /**
   * 请求服务API
   *
   * @author chitanda
   * @date 2021-08-25 16:08:54
   * @protected
   * @param {FastifyRequest} req
   * @param {string} url
   * @param {*} [data]
   * @param {AxiosRequestConfig} [config]
   */
  protected async callFetchAPI(
    req: FastifyRequest,
    res: FastifyReply,
    url: string,
    data?: any,
    config?: AxiosRequestConfig,
  ): Promise<void> {
    url = this.transformationPrams(url, req.query);
    const result = await this.http.post(req, res, Environment.DefaultAPI.Path + url, data, config);
    if (result) {
      const d = result.data;
      res.header('x-page', d.number);
      res.header('x-per-page', d.size);
      res.header('x-total', d.totalElements);
      this.parseFetchResponse(req, res, result);
    }
  }

  /**
   * 请求返回编译
   *
   * @author chitanda
   * @date 2021-08-26 10:08:47
   * @protected
   * @param {FastifyRequest} req
   * @param {FastifyReply} res
   * @param {AxiosResponse<any>} aRes
   * @return {*}  {Promise<void>}
   */
  protected async parseResponse(_req: FastifyRequest, res: FastifyReply, aRes: AxiosResponse<any>): Promise<any> {
    if (aRes) {
      try {
        const headers: Record<string, any> = aRes.headers;
        for (const key in headers) {
          if (Object.prototype.hasOwnProperty.call(headers, key)) {
            const val = headers[key];
            res.header(key, val);
          }
        }
        res.header('connection', 'keep-alive');
        res.status(HttpStatus.OK).send(aRes.data);
      } catch (err) {
        res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err.message);
      }
    }
  }

  /**
   * 请求返回编译
   *
   * @author chitanda
   * @date 2021-08-26 10:08:47
   * @protected
   * @param {FastifyRequest} req
   * @param {FastifyReply} res
   * @param {AxiosResponse<any>} aRes
   * @return {*}  {Promise<void>}
   */
  protected async parseFetchResponse(_req: FastifyRequest, res: FastifyReply, aRes: AxiosResponse<any>): Promise<any> {
    if (aRes) {
      try {
        const headers: Record<string, any> = aRes.headers;
        for (const key in headers) {
          if (Object.prototype.hasOwnProperty.call(headers, key)) {
            const val = headers[key];
            res.header(key, val);
          }
        }
        res.header('connection', 'keep-alive');
        const data = aRes.data;
        res.status(HttpStatus.OK).send(data ? data.content : []);
      } catch (err) {
        res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err.message);
      }
    }
  }

  /**
   * 请求参数转义处理
   *
   * @author chitanda
   * @date 2021-08-27 15:08:20
   * @private
   * @param {string} url
   * @param {*} [opt={}]
   * @return {*}  {string}
   */
  private transformationPrams(url: string, opt: any = {}): string {
    if (notNilEmpty(opt)) {
      const postData: string[] = [];
      const keys: string[] = Object.keys(opt);
      keys.forEach((key: string) => {
        const val: any = opt[key];
        if (notNilEmpty(val)) {
          if (val instanceof Array || val instanceof Object) {
            postData.push(`${key}=${encodeURIComponent(JSON.stringify(val))}`);
          } else {
            postData.push(`${key}=${encodeURIComponent(val)}`);
          }
        }
      });
      const query = postData.join('&');
      if (notNilEmpty(query)) {
        if (url.endsWith('?')) {
          url = `${url}${query}`;
        } else if (url.indexOf('?') !== -1 && url.endsWith('&')) {
          url = `${url}${query}`;
        } else if (url.indexOf('?') !== -1 && !url.endsWith('&')) {
          url = `${url}&${query}`;
        } else {
          url = `${url}?${query}`;
        }
      }
    }
    return url;
  }
}
