import { Eureka, EurekaClient } from 'eureka-js-client';
import axios from 'axios';
import { checkType, jj, random } from './utils';
import * as address from 'address';
const Netmask = require('netmask').Netmask

const ip = address.ip() || '127.0.0.1';

export interface IEurekaObjs {
  [name: string]: string[];
}

export default class Pool {
  private eurekaObjs: IEurekaObjs = {};
  private instances: EurekaClient.EurekaInstanceConfig[] = [];
  constructor(eureka: Eureka, services: string[]) {
    setTimeout(() => {
      this.getEurekas(eureka, services);
    }, 3000);

    setInterval(() => {
      this.filterPool();
    }, 4000);
  }

  /**
   * 遍历eureka
   * @param {Eureka} eureka  Eureka
   * @param {string[]} services 服务名
   */
  private async getEurekas(eureka: Eureka, services: string[]) {
    for (let i = 0; i < services.length; i++) {
      const name = services[i];
      const instances = eureka.getInstancesByAppId(name);
      let tmpList = [];
      instances.map(v => {
        tmpList.push(`http://${v.hostName}:${(<{ $: number }>v.port).$}`);
      });
      this.eurekaObjs[name] = tmpList;
      this.instances.push(...instances);
    }
    // 每30秒刷新eurekaObjs列表
    setTimeout(() => {
      this.getEurekas(eureka, services);
    }, 30000);
  }
  private async filterPool() {
    let { eurekaObjs } = this;
    for (let i in eurekaObjs) {
      const obj = eurekaObjs[i];
      for (let j = 0; j < obj.length; j++) {
        try {
          let params = '';
          if (i === 'duiba-manager-web') {
            params = 'newmanager/'
          }
          const { data } = await axios.get(`${obj[j]}/${params}monitor/check`);
          if (checkType(data, 'String')) {
            if (data.toLowerCase() !== 'ok') {
              eurekaObjs[i].splice(j, 1, undefined);
            }
          } else {
            eurekaObjs[i].splice(j, 1, undefined);
          }
        } catch (e) {
          eurekaObjs[i].splice(j, 1, undefined);
        }
      }
      eurekaObjs[i] = eurekaObjs[i].filter(v => v !== undefined)
    }
  }

  /**
   * 获取所有eureka
   */
  getAllEurekas(): EurekaClient.EurekaInstanceConfig[] {
    return this.instances;
  }

  /**
   * 获取所有实例下的host
   */
  getAllHostName() {
    return this.eurekaObjs;
  }

  private getRandomHost(host: string[]) {
    const len = host.length;
    const num = random(0, len - 1);
    return host[num] || ''
  }

  getHost(name: string | number, localIp: string, filterGroup?: string) {
    const { eurekaObjs, instances } = this;
    const pool = eurekaObjs[name];
    if (!pool) {
      console.error('请先注册service');
      process.exit(1)
    }

    const { NODE_ENV } = process.env;

    if (NODE_ENV === 'prod') {
      return this.filterHybridCloud(instances, pool);
    }

    // 优先走本地
    let hasLocalIp = false;
    let clientIp = '';
    for (let i = 0; i < pool.length; i++) {
      if (pool[i].includes(localIp)) {
        hasLocalIp = true;
        clientIp = pool[i];
      }
    }

    if (hasLocalIp) {
      return clientIp;
    }

    /** 最优先匹配具体标记的，即多环境 */
    let highestPriorityList = []; // 最高优先级  匹配到具体的metadata
    let havePriorityList = []; // 优先转发目标   除具体metadata之外的metadata

    /** 没有标记则默认转发到公共服务 */
    let thirdPriorityList = []; // 第三优先级   runInSingleJarMode=true的是公共服务

    /** 个人服务 */
    let noGroupService = []; // 没有分组标记     没有任何标记

    for (let i in instances) {
      const ins = instances[i];
      const host = `http://${ins.hostName}:${(<{ $: number }>ins.port).$}`;
      const { metadata } = ins;
      const group = metadata['duiba.service.group.key'];
      const runInSingleJarMode = metadata['runInSingleJarMode'] === 'true';

      if (filterGroup && group) { // 如果存在_duibaServiceGroupKey 且存在多场景
        if (group === filterGroup) { // 完全匹配metadata
          highestPriorityList.push(host);
        } else if (group) { // 匹配不到，多场景为最低优先级
          noGroupService.push(host);
        } else {
          if (runInSingleJarMode) {
            havePriorityList.push(host);
          } else {
            thirdPriorityList.push(host);
          }
        }
      } else {
        if (!group) {
          if (runInSingleJarMode) {
            havePriorityList.push(host);
          } else {
            thirdPriorityList.push(host);
          }
        } else {
          noGroupService.push(host);
        }
      }
    }
    highestPriorityList = jj(highestPriorityList, pool);
    havePriorityList = jj(havePriorityList, pool);
    thirdPriorityList = jj(thirdPriorityList, pool);

    let tmpPool: string[] = [];

    if (highestPriorityList.length > 0) {
      // 如果完全匹配
      return this.getRandomHost(highestPriorityList);
    } else if (havePriorityList.length > 0) {
      // 存在优先级高的
      return this.getRandomHost(havePriorityList)
    } else if (thirdPriorityList.length > 0) {
      return this.getRandomHost(thirdPriorityList)
    } else {
      // 同网段的优先
      // @ts-ignore
      const { cidr } = address.interface();
      var block = new Netmask(cidr);
      pool.map(v => {
        if (block.contains(v.split('http://')[1])) {
          tmpPool.push(v)
        }
      });

      if (tmpPool.length === 0) { // 如果不存在同网段，随便
        tmpPool = pool;
      }
      return this.getRandomHost(tmpPool)
    }
  }

  /**
   * 筛选混合云符合条件IP
   * http://cf.dui88.com/pages/viewpage.action?pageId=38788463
   * @param {EurekaClient.EurekaInstanceConfig[]} instances 服务实例
   * @param {*} pool  ip池
   * @returns
   * @memberof Pool
   */
  filterHybridCloud(instances: EurekaClient.EurekaInstanceConfig[], pool: any) {
    let currentApplicationZone = 'defaultZone';

    let aliyunCloud = [];
    let huaweiCloud = [];

    for (let i = 0; i < instances.length; i++) {
      const ins = instances[i];
      const { hostName, metadata } = ins;
      const host = `http://${hostName}:${(<{ $: Number }>ins.port).$}`;


      const { zone } = metadata;

      if (hostName === ip) {
        currentApplicationZone = zone === 'huawei' ? 'huawei' : 'defaultZone';
      }

      // 阿里云机器环注册的metadata为zone=defaultZone，华为云注册的metadata为huawei (假如获取ZONE为空，则视为defaultZone，注册到eureka的zone值也为defaultZone)
      if (zone === 'huawei') {
        huaweiCloud.push(host);
      } else {
        aliyunCloud.push(host);
      }
    }

    aliyunCloud = jj(aliyunCloud, pool);
    huaweiCloud = jj(huaweiCloud, pool);

    const aliyunCloudLength = aliyunCloud.length;
    const huaweiCloudLength = huaweiCloud.length;

    // 如果任一机房没有实例，则去另一个机房随机获取实例
    if (aliyunCloudLength === 0) {
      return this.getRandomHost(huaweiCloud);
    } else if (huaweiCloudLength === 0) {
      return this.getRandomHost(aliyunCloud);
    }

    const [min, max] = [aliyunCloudLength, huaweiCloudLength].sort((a, b) => a - b);

    // 判断服务在两个机房的分布是不是小于3，小于3做同机房优先调用，否则随机调用
    if (max / min < 3) {
      if (currentApplicationZone === 'huawei') {
        return this.getRandomHost(huaweiCloud);
      } else {
        return this.getRandomHost(aliyunCloud);
      }
    } else {
      return this.getRandomHost([...aliyunCloud, ...huaweiCloud])
    }
  }

}
