import { Environment } from '@/environment';
import { NestMiddleware } from '@nestjs/common';
import { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware';
import { GatewayService } from '../service';
import { logger } from '../utils';

/**
 * 请求代理中间件
 *
 * @author chitanda
 * @date 2021-12-10 20:12:15
 * @export
 * @abstract
 * @class ProxyMiddleware
 * @implements {NestMiddleware}
 */
export abstract class ProxyMiddleware implements NestMiddleware {
  /**
   * nacos地址计算服务
   *
   * @author chitanda
   * @date 2021-08-17 19:08:13
   * @private
   * @type {GatewayService}
   */
  private readonly gateway: GatewayService = GatewayService.getInstance();

  /**
   * nacos 服务标识
   *
   * @author chitanda
   * @date 2021-12-10 19:12:08
   * @protected
   * @abstract
   * @type {string}
   */
  protected abstract serviceTag: string;

  /**
   * 计算的 service name 名称
   *
   * @author chitanda
   * @date 2021-12-10 19:12:26
   * @protected
   * @type {string}
   */
  protected serviceName?: string;

  /**
   * 代理对象缓存
   *
   * @author chitanda
   * @date 2022-03-25 10:03:29
   * @protected
   * @type {Map<string, RequestHandler>}
   */
  protected proxyMap: Map<string, RequestHandler> = new Map();

  use(req: any, res: any, next: () => void): void {
    const host = this.getHost();
    if (!host) {
      return next();
    }
    let proxy: RequestHandler;
    if (this.proxyMap.has(host)) {
      proxy = this.proxyMap.get(host);
    } else {
      const pathKey = `^${Environment.BasePath}/`;
      const path = `/`;
      proxy = createProxyMiddleware({
        target: host,
        changeOrigin: true,
        pathRewrite: { [pathKey]: path },
      });
      this.proxyMap.set(host, proxy);
    }
    logger.debug(`${req.originalUrl} => ${host}`);
    proxy(req, res, next);
  }

  /**
   * 从 nacos 获取服务地址
   *
   * @author chitanda
   * @date 2021-12-10 19:12:47
   * @protected
   * @return {*}  {(string | null)}
   */
  protected getHost(): string | null {
    if (this.serviceTag) {
      if (!this.serviceName) {
        this.serviceName = this.gateway.getServiceId(this.serviceTag);
      }
      const host = this.gateway.getHost(this.serviceName);
      if (host) {
        return host;
      }
    }
    return null;
  }
}
