/*
 * Copyright 1999-2023 Alibaba Group Holding Ltd.
 *
 * 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.alibaba.nacos.plugin.control.rule;

import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.plugin.control.ControlManagerCenter;
import com.alibaba.nacos.plugin.control.Loggers;
import com.alibaba.nacos.plugin.control.connection.ConnectionControlManager;
import com.alibaba.nacos.plugin.control.connection.rule.ConnectionControlRule;
import com.alibaba.nacos.plugin.control.event.ConnectionLimitRuleChangeEvent;
import com.alibaba.nacos.plugin.control.event.TpsControlRuleChangeEvent;
import com.alibaba.nacos.plugin.control.rule.storage.RuleStorageProxy;
import com.alibaba.nacos.plugin.control.tps.TpsControlManager;
import com.alibaba.nacos.plugin.control.tps.rule.TpsControlRule;
import org.slf4j.Logger;

/**
 * control rule activator.
 *
 * @author shiyiyue
 */
public class ControlRuleChangeActivator {
    
    private static final Logger LOGGER = Loggers.CONTROL;
    
    TpsRuleChangeSubscriber tpsRuleChangeSubscriber = new TpsRuleChangeSubscriber();
    
    ConnectionRuleChangeSubscriber connectionRuleChangeSubscriber = new ConnectionRuleChangeSubscriber();
    
    public ControlRuleChangeActivator() {
        NotifyCenter.registerSubscriber(tpsRuleChangeSubscriber);
        NotifyCenter.registerSubscriber(connectionRuleChangeSubscriber);
    }
    
    static class TpsRuleChangeSubscriber extends Subscriber<TpsControlRuleChangeEvent> {
        
        @Override
        public void onEvent(TpsControlRuleChangeEvent event) {
            String pointName = event.getPointName();
            LOGGER.info("Tps control rule change event receive,pointName={}, external={} ", pointName,
                    event.isExternal());
            if (event.getPointName() == null) {
                return;
            }
            try {
                RuleStorageProxy ruleStorageProxy = ControlManagerCenter.getInstance().getRuleStorageProxy();
                
                if (event.isExternal()) {
                    if (ruleStorageProxy.getExternalStorage() != null) {
                        String persistTpsRule = ruleStorageProxy.getExternalStorage().getTpsRule(pointName);
                        ruleStorageProxy.getLocalDiskStorage().saveTpsRule(pointName, persistTpsRule);
                    } else {
                        Loggers.CONTROL.info(
                                "No external rule storage found,will load local disk instead,point name={}",
                                event.getPointName());
                    }
                    
                }
                String tpsRuleContent = ruleStorageProxy.getLocalDiskStorage().getTpsRule(pointName);
                TpsControlManager tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager();
                TpsControlRule tpsControlRule = StringUtils.isBlank(tpsRuleContent) ? new TpsControlRule()
                        : tpsControlManager.getTpsControlRuleParser().parseRule(tpsRuleContent);
                
                tpsControlManager.applyTpsRule(pointName, tpsControlRule);
                
            } catch (Exception e) {
                LOGGER.warn("Tps control rule apply error ,error= ", e);
            }
            
        }
        
        @Override
        public Class<? extends Event> subscribeType() {
            return TpsControlRuleChangeEvent.class;
        }
    }
    
    static class ConnectionRuleChangeSubscriber extends Subscriber<ConnectionLimitRuleChangeEvent> {
        
        @Override
        public void onEvent(ConnectionLimitRuleChangeEvent event) {
            LOGGER.info("connection limit rule change event receive ,external:{}", event.isExternal());
            
            try {
                
                RuleStorageProxy ruleStorageProxy = ControlManagerCenter.getInstance().getRuleStorageProxy();
                
                if (event.isExternal()) {
                    if (ruleStorageProxy.getExternalStorage() != null) {
                        String connectionRule = ruleStorageProxy.getExternalStorage().getConnectionRule();
                        ruleStorageProxy.getLocalDiskStorage().saveConnectionRule(connectionRule);
                    } else {
                        Loggers.CONTROL.info("No external rule storage found,will load local disk instead");
                        
                    }
                    
                }
                String limitRule = ruleStorageProxy.getLocalDiskStorage().getConnectionRule();
                
                Loggers.CONTROL.info("start to apply connection rule content {}", limitRule);
                ConnectionControlManager controlManager = ControlManagerCenter.getInstance()
                        .getConnectionControlManager();
                ConnectionControlRule connectionControlRule =
                        StringUtils.isBlank(limitRule) ? new ConnectionControlRule()
                                : controlManager.getConnectionControlRuleParser().parseRule(limitRule);
                Loggers.CONTROL.info("end to  apply connection rule content ");
                
                if (connectionControlRule != null) {
                    controlManager.applyConnectionLimitRule(connectionControlRule);
                } else {
                    LOGGER.info("Parse rule is null,Ignore illegal rule  :{}", limitRule);
                }
                
            } catch (Exception e) {
                LOGGER.error("Fail to parse connection limit rule ,persit:{}", event.isExternal(), e);
            }
        }
        
        @Override
        public Class<? extends Event> subscribeType() {
            return ConnectionLimitRuleChangeEvent.class;
        }
    }
    
}
