package net.dubboclub.circuitbreaker;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.utils.ConfigUtils;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import org.apache.commons.lang.StringUtils;

import java.net.InetAddress;

/**
 * Created by bieber on 2015/6/15.
 */
public class Config {

    //默认情况下1分钟内出现10个异常，同时默认情况下当满足1分钟内10个异常，然后每发生10次错误就会进行一次重试，检查服务是否恢复
    private static final String DEFAULT_BREAK_LIMIT="10";

    private static final String DEFAULT_RETRY_FREQUENCY="10";

    protected static final String DUBBO_REFERENCE_PREFIX="dubbo.reference.";

    private static final String DUBBO_REFERENCE_CIRCUIT_BREAKER_SWITCH=DUBBO_REFERENCE_PREFIX+"circuit.breaker";

    /**
     * 获取当前调用的服务方法是否开启了服务降级功能
     *  @param invoker
     * @param invocation
     * @return
     */
    public static boolean checkFunctionSwitch(Invoker<?> invoker, Invocation invocation){
        //先动态从调用地址里面获取开关配置信息，这个配置可以在配置中心的动态配置{methodName.}circuit.bread=true/false
        String circuitBreak = invoker.getUrl().getMethodParameter(invocation.getMethodName(),"circuit.break");
        if(StringUtils.isEmpty(circuitBreak)){
            String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY);
            String method = invocation.getMethodName();
            StringBuffer interfaceConfig = new StringBuffer(DUBBO_REFERENCE_PREFIX);
            interfaceConfig.append(interfaceName);
            StringBuffer methodConfig = new StringBuffer(interfaceConfig.toString());
            methodConfig.append(".").append(method);
            return getSwitch(interfaceConfig,methodConfig);
        }
        return Boolean.parseBoolean(StringUtils.trim(circuitBreak));
    }
    
    public static int getBreakLimit(Invoker<?> invoker,Invocation invocation){
        String dynamicConfig = invoker.getUrl().getMethodParameter(invocation.getMethodName(),"break.limit");
        if(StringUtils.isEmpty(dynamicConfig)){
            return getBreakLimit(getInterfacePropertyName(invoker),getMethodPropertyName(invoker,invocation));
        }
        int limit =  Integer.parseInt(dynamicConfig);
        int retries = invoker.getUrl().getParameter(Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES);
        retries++;
        return limit*retries;
    }
    public static StringBuffer getInterfacePropertyName(Invoker<?> invoker){
        String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY);
        StringBuffer interfaceConfig = new StringBuffer(DUBBO_REFERENCE_PREFIX);
        interfaceConfig.append(interfaceName);
        return interfaceConfig;
    }

    public static StringBuffer getMethodPropertyName(Invoker<?> invoker, Invocation invocation){
        String interfaceName = invoker.getUrl().getParameter(Constants.INTERFACE_KEY);
        String method = invocation.getMethodName();
        StringBuffer interfaceConfig = new StringBuffer(DUBBO_REFERENCE_PREFIX);
        interfaceConfig.append(interfaceName);
        StringBuffer methodConfig = new StringBuffer(interfaceConfig.toString());
        methodConfig.append(".").append(method);
        return methodConfig;
    }
    
    public static InetAddress getLocalAddress(){
        return NetUtils.getLocalAddress();
    }
    /**
     * 获取某个方法或者某个接口的判定为出现异常的次数
     * 提供在配置中心和dubbo.properties两种途径配置
     * 如果两个地方均有配置，配置中心的为准
     * @param interfaceConfig
     * @param methodConfig
     * @return
     */
    public static int getBreakLimit(StringBuffer interfaceConfig,StringBuffer methodConfig){
        methodConfig.append(".break.limit");
        interfaceConfig.append(".break.limit");
        String breakLimitConf = ConfigUtils.getProperty(methodConfig.toString(), ConfigUtils.getProperty(interfaceConfig.toString(), ConfigUtils.getProperty("dubbo.reference.default.break.limit", DEFAULT_BREAK_LIMIT)));
        return Integer.parseInt(breakLimitConf);
    }

    /**
     * 获取某个方法或者接口是否开启服务降级的功能，默认是开启降级功能
     * 提供在配置中心和dubbo.properties两种途径配置
     * 如果两个地方均有配置，配置中心的为准
     * @param interfaceConfig
     * @param methodConfig
     * @return
     */
    public static boolean getSwitch(StringBuffer interfaceConfig, StringBuffer methodConfig){
        methodConfig.append(".circuit.break");
        interfaceConfig.append(".circuit.break");
        //从dubbo.properties的配置中获取
        String switchConfig = ConfigUtils.getProperty(methodConfig.toString(), ConfigUtils.getProperty(interfaceConfig.toString(), ConfigUtils.getProperty("dubbo.reference.circuit.break", "false")));
        return Boolean.parseBoolean(switchConfig);
    }

    public static int getRetryFrequency(Invoker<?> invoker,Invocation invocation){
        String retryFrequency = invoker.getUrl().getMethodParameter(invocation.getMethodName(),"retry.frequency");
        if(StringUtils.isEmpty(retryFrequency)){
            return getRetryFrequency(getInterfacePropertyName(invoker),getMethodPropertyName(invoker,invocation));
        }
        return Integer.parseInt(retryFrequency);
    }

    /**
     * 获取某个方法或者接口重试频次，及每出现多少次异常，就重试一次远程接口
     * 提供在配置中心和dubbo.properties两种途径配置
     * 如果两个地方均有配置，配置中心的为准
     * @param interfaceConfig
     * @param methodConfig
     * @return
     */
    public static int getRetryFrequency(StringBuffer interfaceConfig, StringBuffer methodConfig){
        methodConfig.append(".retry.frequency");
        interfaceConfig.append(".retry.frequency");
        String retryFrequencyConf = ConfigUtils.getProperty(methodConfig.toString(), ConfigUtils.getProperty(interfaceConfig.toString(), ConfigUtils.getProperty("dubbo.reference.default.retry.frequency", DEFAULT_RETRY_FREQUENCY)));
        return Integer.parseInt(retryFrequencyConf);
    }
}

