package ru.yandex.webmaster3.core.metrika.counters;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.util.WwwUtil;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

import static ru.yandex.webmaster3.core.metrika.counters.CounterActionTypeEnum.*;

/**
 * Created by ifilippov5 on 23.10.17.
 */
public class MetrikaCountersUtil {
    private static final Logger log = LoggerFactory.getLogger(MetrikaCountersUtil.class);

    public static CounterBindingStateEnum nextState(CounterBindingStateEnum state, CounterActionTypeEnum action) {
        return JUMPS.get(Pair.of(state, action));
    }

    public static Stream<WebmasterHostId> generateHostIds(String domain) {
        String asciiDomain = domainToCanonicalAscii(domain);
        //TODO refactor
        return Stream.of(
                new WebmasterHostId(WebmasterHostId.Schema.HTTP, asciiDomain, WebmasterHostId.DEFAULT_HTTP_PORT),
                new WebmasterHostId(WebmasterHostId.Schema.HTTP, "www." + asciiDomain, WebmasterHostId.DEFAULT_HTTP_PORT),
                new WebmasterHostId(WebmasterHostId.Schema.HTTPS, asciiDomain, WebmasterHostId.DEFAULT_HTTPS_PORT),
                new WebmasterHostId(WebmasterHostId.Schema.HTTPS, "www." + asciiDomain, WebmasterHostId.DEFAULT_HTTPS_PORT)
        );
    }

    public static WebmasterHostId domainToHostId(String domain) {
        String asciiDomain = domainToCanonicalAscii(domain);
        return IdUtils.urlToHostId(asciiDomain);
    }

    public static String hostToReadableDomain(WebmasterHostId hostId) {
        if (WwwUtil.isWWW(hostId.getReadableHostname())) {
            return WwwUtil.switchWWW(hostId.getReadableHostname());
        }
        return hostId.getReadableHostname();
    }

    public static String hostToPunycodeDomain(WebmasterHostId hostId) {
        String punycodeHostname = hostId.getPunycodeHostname();
        if (WwwUtil.isWWW(punycodeHostname)) {
            return WwwUtil.switchWWW(punycodeHostname);
        }
        return punycodeHostname;
    }

    public static String domainToCanonicalAscii(String domain) {
        String asciiDomain = IdUtils.IDN.toASCII(domain.toLowerCase());
        if (WwwUtil.isWWW(asciiDomain)) {
            return WwwUtil.switchWWW(asciiDomain);
        }

        return asciiDomain;
    }

    public static CounterActionTypeEnum getActionFromMetrikaType(CounterRequestTypeEnum action) {
        return MAP_REQUEST_ACTION_METRIKA.get(action);
    }

    public static CounterActionTypeEnum getActionFromWebmasterType(CounterRequestTypeEnum action) {
        return MAP_REQUEST_ACTION_WEBMASTER.get(action);
    }
    
    private static Map<CounterRequestTypeEnum, CounterActionTypeEnum> MAP_REQUEST_ACTION_METRIKA =
            new EnumMap<>(CounterRequestTypeEnum.class);
    static {
        MAP_REQUEST_ACTION_METRIKA.put(CounterRequestTypeEnum.CREATE, CounterActionTypeEnum.M_REQUEST);
        MAP_REQUEST_ACTION_METRIKA.put(CounterRequestTypeEnum.REJECT, CounterActionTypeEnum.M_REJECT);
        MAP_REQUEST_ACTION_METRIKA.put(CounterRequestTypeEnum.CONFIRM, CounterActionTypeEnum.M_REQUEST);
        MAP_REQUEST_ACTION_METRIKA.put(CounterRequestTypeEnum.DISMISS, CounterActionTypeEnum.M_REJECT);
        MAP_REQUEST_ACTION_METRIKA.put(CounterRequestTypeEnum.CANCEL, CounterActionTypeEnum.M_REJECT);
    }

    private static Map<CounterRequestTypeEnum, CounterActionTypeEnum> MAP_REQUEST_ACTION_WEBMASTER =
            new EnumMap<>(CounterRequestTypeEnum.class);
    static {
        MAP_REQUEST_ACTION_WEBMASTER.put(CounterRequestTypeEnum.CREATE, CounterActionTypeEnum.W_REQUEST);
        MAP_REQUEST_ACTION_WEBMASTER.put(CounterRequestTypeEnum.REJECT, CounterActionTypeEnum.W_REJECT);
        MAP_REQUEST_ACTION_WEBMASTER.put(CounterRequestTypeEnum.CONFIRM, CounterActionTypeEnum.W_REQUEST);
        MAP_REQUEST_ACTION_WEBMASTER.put(CounterRequestTypeEnum.DISMISS, CounterActionTypeEnum.W_REJECT);
        MAP_REQUEST_ACTION_WEBMASTER.put(CounterRequestTypeEnum.CANCEL, CounterActionTypeEnum.W_REJECT);
    }
    
    /**
     * Переходы в автомате состояний счетчика метрики
     */
    public static Map<Pair<CounterBindingStateEnum, CounterActionTypeEnum>, CounterBindingStateEnum> JUMPS = new HashMap<>();
    static {
        JUMPS.put(Pair.of(CounterBindingStateEnum.NONE, M_REJECT), CounterBindingStateEnum.NONE);
        JUMPS.put(Pair.of(CounterBindingStateEnum.NONE, M_REQUEST), CounterBindingStateEnum.METRIKA_REQUEST);
        JUMPS.put(Pair.of(CounterBindingStateEnum.NONE, W_REQUEST), CounterBindingStateEnum.WEBMASTER_REQUEST);
        JUMPS.put(Pair.of(CounterBindingStateEnum.NONE, W_REJECT), CounterBindingStateEnum.NONE);

        JUMPS.put(Pair.of(CounterBindingStateEnum.METRIKA_REQUEST, W_REQUEST), CounterBindingStateEnum.APPROVED);
        JUMPS.put(Pair.of(CounterBindingStateEnum.METRIKA_REQUEST, W_REJECT), CounterBindingStateEnum.NONE);
        JUMPS.put(Pair.of(CounterBindingStateEnum.METRIKA_REQUEST, M_REJECT), CounterBindingStateEnum.NONE);
        JUMPS.put(Pair.of(CounterBindingStateEnum.METRIKA_REQUEST, M_REQUEST), CounterBindingStateEnum.METRIKA_REQUEST);

        JUMPS.put(Pair.of(CounterBindingStateEnum.APPROVED, W_REQUEST), CounterBindingStateEnum.APPROVED);
        JUMPS.put(Pair.of(CounterBindingStateEnum.APPROVED, W_REJECT), CounterBindingStateEnum.NONE);
        JUMPS.put(Pair.of(CounterBindingStateEnum.APPROVED, M_REJECT), CounterBindingStateEnum.NONE);
        JUMPS.put(Pair.of(CounterBindingStateEnum.APPROVED, M_REQUEST), CounterBindingStateEnum.APPROVED);

        JUMPS.put(Pair.of(CounterBindingStateEnum.WEBMASTER_REQUEST, M_REJECT), CounterBindingStateEnum.METRIKA_REJECT);
        JUMPS.put(Pair.of(CounterBindingStateEnum.WEBMASTER_REQUEST, M_REQUEST), CounterBindingStateEnum.APPROVED);
        JUMPS.put(Pair.of(CounterBindingStateEnum.WEBMASTER_REQUEST, W_REQUEST), CounterBindingStateEnum.WEBMASTER_REQUEST);
        JUMPS.put(Pair.of(CounterBindingStateEnum.WEBMASTER_REQUEST, W_REJECT), CounterBindingStateEnum.NONE);

        JUMPS.put(Pair.of(CounterBindingStateEnum.METRIKA_REJECT, M_REJECT), CounterBindingStateEnum.METRIKA_REJECT);
        JUMPS.put(Pair.of(CounterBindingStateEnum.METRIKA_REJECT, M_REQUEST), CounterBindingStateEnum.APPROVED);
        JUMPS.put(Pair.of(CounterBindingStateEnum.METRIKA_REJECT, W_REQUEST), CounterBindingStateEnum.METRIKA_REJECT);
        JUMPS.put(Pair.of(CounterBindingStateEnum.METRIKA_REJECT, W_REJECT), CounterBindingStateEnum.NONE);
    }
}
