package org.skdev.distops.core.proxy;

import lombok.extern.slf4j.Slf4j;
import org.skdev.distops.annotation.DistPolicy;
import org.skdev.distops.core.exception.DistOpsException;
import org.skdev.distops.core.policy.*;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class DistOpsInvocationHandler implements InvocationHandler {

    private Map<DistPolicy, RemoteServiceSelector> selectorMap = new HashMap<>();

    private RemoteServiceContainer remoteServiceContainer;
    private DistPolicyManager distPolicyManager;

    public DistOpsInvocationHandler(RemoteServiceContainer remoteServiceContainer, DistPolicyManager distPolicyManager) {
        this.remoteServiceContainer = remoteServiceContainer;
        this.distPolicyManager = distPolicyManager;
        selectorMap.put(DistPolicy.RANDOM, new RandomSelector());
        selectorMap.put(DistPolicy.RR, new RoundRobinSelector());
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        log.debug("invoking remote service {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());

        Class serviceInterface = method.getDeclaringClass();

        if (!distPolicyManager.contains(serviceInterface)) {
            throw new DistOpsException(String.format("distPolicy not set for %s", serviceInterface));
        }

        List<RmiProxy> rmiProxyList = remoteServiceContainer.getServiceList(serviceInterface);
        if (rmiProxyList == null || rmiProxyList.size() == 0) {
            throw new DistOpsException("remote service not exists");
        }

        DistPolicy policy = distPolicyManager.get(serviceInterface);
        RemoteServiceSelector selector = selectorMap.get(policy);

        RmiProxy rmiProxy = selector.select(rmiProxyList);

        return method.invoke(rmiProxy.getTarget(), args);
    }
}
