"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GrayUtils = void 0;
const CommUtils_1 = require("./CommUtils");
const GlobalZoneUtils_1 = require("./GlobalZoneUtils");
const MsLoadBalanceUtils_1 = require("./MsLoadBalanceUtils");
const MsConfig_1 = require("../mslib/MsConfig");
const Logger_1 = require("../mslib/Logger");
const HttpUtils_1 = require("./HttpUtils");
/**
 * 灰度客户端，从主注册中心中拉取灰度配置，间隔15秒刷新一下缓存
 * @author wufenglin
 */
class GrayUtils {
    constructor() {
        this.envGrayConfigCache = new Map();
        this.grayTargetCache = new Map();
        this.updateCachePeriod = 1000 * 15;
        this.scheduleAtUpdateCache(this.updateCachePeriod);
    }
    /**
    * 定时更新缓存
    * @param period 执行周期 毫秒
    */
    scheduleAtUpdateCache(period) {
        setTimeout(() => __awaiter(this, void 0, void 0, function* () {
            yield this.updateCache();
            this.scheduleAtUpdateCache(period);
        }), period);
    }
    /**
        * 从Zone 中获取灰度标签
        * @param headers
        */
    static getGrayLabel() {
        try {
            return GlobalZoneUtils_1.GlobalZoneUtils.getGrayLabel();
        }
        catch (e) {
        }
    }
    grayHandle(msBalance) {
        return __awaiter(this, void 0, void 0, function* () {
            if (msBalance == null) {
                return;
            }
            if (!this.isSideCarBalance(msBalance)) {
                return msBalance;
            }
            const minorVersion = this.parseMinorVersion(msBalance.msRunning.projectVersion);
            const pid = msBalance.msRunning.pid;
            const versionGrayKey = pid + '_' + minorVersion;
            const grayConfig = this.getGrayProjectConfig(versionGrayKey);
            if (grayConfig == null) {
                return msBalance;
            }
            if (!this.hasNeedGray(grayConfig, versionGrayKey)) {
                return msBalance;
            }
            const grayProjectVersions = grayConfig["versions"];
            const grayVersionKey = grayProjectVersions[versionGrayKey];
            try {
                const runningTargets = yield this.getGrayMsTarget(grayVersionKey);
                if (!runningTargets || runningTargets.length == 0) {
                    return msBalance;
                }
                const grayBalance = new MsLoadBalanceUtils_1.SideCarLoadBalance(msBalance.msRunning);
                grayBalance.targetList = runningTargets;
                Logger_1.logger.info("调用服务消费者灰度主机----grayLabel:%s, grayVersionKey:%s", grayConfig, grayVersionKey);
                return grayBalance;
            }
            catch (e) {
                throw new Error(`获取微服务(${pid}/${minorVersion})灰度主机异常 : ${e}`);
            }
        });
    }
    getGrayProjectConfig(versionGrayKey) {
        let grayConfig = this.envGrayConfigCache.get(MsConfig_1.default.envCode);
        if (grayConfig == null) {
            return null;
        }
        const grayLabel = GrayUtils.getGrayLabel();
        if (CommUtils_1.CommUtils.isNotEmpty(grayLabel)) {
            let grayConfigKey = grayConfig[grayLabel];
            if (CommUtils_1.CommUtils.isEmpty(grayConfigKey)) {
                return null;
            }
            return grayConfig[grayConfigKey];
        }
        else {
            return grayConfig[versionGrayKey];
        }
    }
    /**
     * 判断是否SideCar客户端负载均衡，消费者直接调用生产者模式，不通过网关情况下，须由客户端处理灰度逻辑，否则由网关统一处理
     */
    isSideCarBalance(msBalance) {
        let target = msBalance.getTarget();
        if (CommUtils_1.CommUtils.getGatewayUrl() && target && CommUtils_1.CommUtils.getGatewayUrl() === target.gatewayUrl) {
            return true;
        }
        return false;
    }
    parseMinorVersion(version) {
        return CommUtils_1.CommUtils.leftStrEx(version, ".");
    }
    /**
     * 判断是否需要灰度
     *
     * @param grayConfig     灰度配置
     * @param versionGrayKey stc.ms.UserAdmin_1.2
     * @return
     */
    hasNeedGray(grayConfig, versionGrayKey) {
        //如果当前关联版本中不含当前服务提供者，则无需灰度
        const grayProjectVersions = grayConfig['versions'];
        if (grayProjectVersions == null || !grayProjectVersions[versionGrayKey]) {
            return false;
        }
        //如果请求头中存在灰度标，则关联版本也需要灰度
        const grayLabel = GrayUtils.getGrayLabel();
        if (CommUtils_1.CommUtils.isNotEmpty(grayLabel)) {
            return true;
        }
        //通过灰度步进规则判断是否需要灰度
        const grayRule = this.findGrayRule(grayConfig);
        const grayPercent = grayRule['grayPercent'];
        return CommUtils_1.CommUtils.random(1, 100) <= grayPercent;
    }
    /**
     * 查找当前时间匹配的灰度步进规则
     *
     * @param grayConfig
     * @return
     */
    findGrayRule(grayConfig) {
        const grayRules = grayConfig['grayRules'];
        if (grayRules == null) {
            return null;
        }
        const now = new Date().getTime();
        for (let i = 0; i < grayRules.length; i++) {
            const rule = grayRules[i];
            if (rule["startTime"] < now && rule["endTime"] > now) {
                return rule;
            }
        }
        return null;
    }
    /**
   * 更新缓存
   */
    updateCache() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.updateEnvGrayCache();
            yield this.updateTargetCache();
        });
    }
    updateEnvGrayCache() {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const data = yield this.loadEnvGrayConfig(MsConfig_1.default.envCode);
                if (data) {
                    this.envGrayConfigCache.set(MsConfig_1.default.envCode, data);
                }
                else {
                    this.envGrayConfigCache.delete(MsConfig_1.default.envCode);
                }
            }
            catch (e) {
                Logger_1.logger.error("updateEnvGrayCache error", e);
            }
        });
    }
    updateTargetCache() {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const keys = this.grayTargetCache.keys();
                for (let grayVersionKey of keys) {
                    const targets = yield this.loadGrayMsTarget(grayVersionKey);
                    if (targets) {
                        this.grayTargetCache.set(grayVersionKey, targets);
                    }
                    else {
                        this.grayTargetCache.delete(grayVersionKey);
                    }
                }
            }
            catch (e) {
                Logger_1.logger.error("updateTargetCache error", e);
            }
        });
    }
    getGrayMsTarget(grayVersionKey) {
        return __awaiter(this, void 0, void 0, function* () {
            let result = this.grayTargetCache.get(grayVersionKey);
            if (result) {
                return result;
            }
            else {
                result = yield this.loadGrayMsTarget(grayVersionKey);
                this.grayTargetCache.set(grayVersionKey, result);
                return result;
            }
        });
    }
    /**
    * 获取灰度服务主机列表
    *
    * @param grayVersionKey
    * @return
    */
    loadGrayMsTarget(grayVersionKey) {
        return __awaiter(this, void 0, void 0, function* () {
            const headers = {
                'grayVersionKey': grayVersionKey
            };
            CommUtils_1.CommUtils.sign(headers, MsConfig_1.default.envCode, MsConfig_1.default.appKey);
            const response = yield HttpUtils_1.default.get(MsConfig_1.default.masterRegisterUrl + '/getGrayMsTarget', {}, headers);
            if (!response.success) {
                return undefined;
            }
            if (CommUtils_1.CommUtils.isEmpty(response.data)) {
                return;
            }
            Logger_1.logger.info("加载灰度主机成功----->{}", response.data);
            return JSON.parse(response.data);
        });
    }
    loadEnvGrayConfig(envCode) {
        return __awaiter(this, void 0, void 0, function* () {
            const headers = {
                'envCode': envCode
            };
            CommUtils_1.CommUtils.sign(headers, MsConfig_1.default.envCode, MsConfig_1.default.appKey);
            const response = yield HttpUtils_1.default.get(MsConfig_1.default.masterRegisterUrl + '/getGrayConfig', {}, headers);
            if (!response.success) {
                return undefined;
            }
            if (CommUtils_1.CommUtils.isEmpty(response.data)) {
                return;
            }
            Logger_1.logger.info("加载灰度配置成功----->{}", response.data);
            return JSON.parse(response.data);
        });
    }
}
exports.GrayUtils = GrayUtils;
