/*
 * Copyright (c) 2019 Baidu, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.baidu.formula.route.spring.boot.route.listener;

import static com.baidu.formula.route.spring.boot.config.RouteConstants.CONFIG_NAMESPACE;
import static com.baidu.formula.route.spring.boot.config.RouteConstants.CONFIG_RULE_CLASS;
import static com.baidu.formula.route.spring.boot.config.RouteConstants.ROUTE_PROPERTY_SOURCE;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;

import com.baidu.formula.route.spring.boot.route.RouteMatcher;
import com.baidu.formula.route.spring.boot.route.env.RoutePropertySource;
import com.baidu.formula.route.spring.boot.route.irule.IRuleInfo;
import com.baidu.formula.route.spring.boot.route.property.FormulaRouteProperty;
import com.baidu.formula.route.spring.boot.route.property.RouteProperties;
import com.google.common.collect.Maps;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.WeightedResponseTimeRule;

/**
 * 路由规则监听器
 */
public class RouteListener implements ApplicationListener<EnvironmentChangeEvent>, ApplicationContextAware {
    private static final Logger LOGGER = LoggerFactory.getLogger(RouteListener.class);

    private ApplicationContext applicationContext;

    private SpringClientFactory springClientFactory;

    private RouteProperties routeProperties;

    private ConfigurableEnvironment configurableEnvironment;

    private RouteMatcher routeMatcher;

    public RouteListener(ApplicationContext applicationContext, SpringClientFactory springClientFactory,
                         RouteProperties routeProperties, ConfigurableEnvironment configurableEnvironment,
                         RouteMatcher routeMatcher) {
        this.applicationContext = applicationContext;
        this.springClientFactory = springClientFactory;
        this.routeProperties = routeProperties;
        this.configurableEnvironment = configurableEnvironment;
        this.routeMatcher = routeMatcher;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 监听EnvironmentChangeEvent 事件,更改相关环境变量
     * @param event
     */
    @Override
    @EventListener(EnvironmentChangeEvent.class)
    public void onApplicationEvent(EnvironmentChangeEvent event) {
        try {
            LOGGER.info("environment change.");
            Map<String, Object> propertySource = Maps.newHashMap();
            if (!routeMatcher.match()) {
                LOGGER.info("this route rules does not match this instance.");
                return;
            }
            // 多条路由规则已先后顺序进行匹配
            FormulaRouteProperty formulaRouteProperty = routeMatcher.getMatchedFormulaRouteProperty();
            // 获取新的负载均衡策略
            String iRuleName = formulaRouteProperty.getLoadbalance();
            String destServiceName = formulaRouteProperty.getDestServiceName();
            IRule oldRule = springClientFactory.getInstance(destServiceName, IRule.class);
            if (oldRule instanceof WeightedResponseTimeRule) {
                // 关闭线程池
                ((WeightedResponseTimeRule) oldRule).shutdown();
            }
            // 清理ribbon 中 所有的client的负载均衡器配置,更改环境变量值,等待下次重新加载client的负载均衡配置
            springClientFactory.destroy();

            // 按照ribbon的规范,配置IRule
            String configClientRule = destServiceName + "." + CONFIG_NAMESPACE + "." + CONFIG_RULE_CLASS;

            propertySource.put(configClientRule, IRuleInfo.getRulePath(iRuleName));
            // 加入至环境变量中
            this.configurableEnvironment.getPropertySources().addFirst(new RoutePropertySource(ROUTE_PROPERTY_SOURCE,
                    propertySource));
        } catch (Exception e) {
            LOGGER.error("refresh route rule exception: {}", e);
        }

    }

}