package ru.yandex.webmaster3.core.turbo.model.analytics;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.BooleanNode;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;

import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.core.util.json.JsonMapping;
import ru.yandex.webmaster3.core.util.json.polymorphic.Polymorphic;

/**
 * Created by Oleg Bazdyrev on 05/07/2017.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class AnalyticsSettings implements Polymorphic<AnalyticsSystemType> {

    public static final TypeReference<List<AnalyticsSettings>> LIST_REFERENCE = new TypeReference<>() {
    };
    private final AnalyticsSystemType type;
    private final String value;

    @JsonCreator
    public AnalyticsSettings(@JsonProperty("type") AnalyticsSystemType type,
                             @JsonProperty("value") String value) {
        this.type = type;
        this.value = value;
    }

    @Override
    @Description("Тип системы статистики")
    public AnalyticsSystemType getType() {
        return type;
    }

    @Description("Идентификатор или параметры счетчика")
    public String getValue() {
        return value;
    }

    @Description("Что представляет из себя value")
    public AnalyticsDisplayType getDisplayType() {
        return type.getDisplayType();
    }

    /**
     * Преобразование в Json для экспорта
     *
     * @return
     */
    public ObjectNode toJson() {
        ObjectNode result = JsonMapping.OM.createObjectNode();
        result.set("id", new TextNode(getValue()));
        result.set("type", new TextNode(type.getDisplayName()));
        return result;
    }

    public static final class MetrikaCounterSettings extends AnalyticsSettings {

        private final boolean webvisor;

        public MetrikaCounterSettings(@JsonProperty("value") String value,
                                      @JsonProperty("webvisor") boolean webvisor) {
            super(AnalyticsSystemType.YANDEX, value);
            this.webvisor = webvisor;
        }

        @Description("Включен ли Вебвизор")
        public boolean isWebvisor() {
            return webvisor;
        }

        @Override
        public ObjectNode toJson() {
            ObjectNode result = super.toJson();
            result.set("id", new TextNode(getValue()));
            result.set("webvisor", BooleanNode.valueOf(webvisor));
            return result;
        }
    }

    public static final class LiveInternetCounterSettings extends AnalyticsSettings {

        public LiveInternetCounterSettings(@JsonProperty("value") String value) {
            super(AnalyticsSystemType.LIVEINTERNET, value);
        }

        public ObjectNode toJson() {
            ObjectNode result = JsonMapping.OM.createObjectNode();
            result.set("params", new TextNode(getValue()));
            result.set("type", new TextNode(getType().getDisplayName()));
            return result;
        }
    }

    @Getter
    public static final class GoogleAnalyticsSettings extends AnalyticsSettings {

        @Description("Включить екоммерс отчеты для гугл аналитикс")
        private final Boolean ecommerce;

        @Description("Измерение")
        private final String dimension;

        @Description("Стратегия междоменного отслеживания")
        private final Strategy strategy;

        @Description("Домены междоменного отслеживания, перечисленные через запятую")
        private final String domains;

        // нужно так как может в данный момент может существовать только 1 счетик с расширенными настройками
        private final Boolean withExtendedSettings;

        public GoogleAnalyticsSettings(@JsonProperty("value") String value,
                                       @JsonProperty("ecommerce") Boolean ecommerce,
                                       @JsonProperty("dimension") String dimension,
                                       @JsonProperty("strategy") Strategy strategy,
                                       @JsonProperty("domains") String domains,
                                       @JsonProperty("withExtendedSettings") Boolean withExtendedSettings) {
            super(AnalyticsSystemType.GOOGLE, value);
            this.ecommerce = ecommerce;
            this.dimension = StringUtils.stripToNull(dimension);
            this.strategy = Objects.requireNonNullElse(strategy, Strategy.NONE);
            this.domains = StringUtils.stripToNull(domains);
            this.withExtendedSettings = BooleanUtils.toBoolean(withExtendedSettings);
        }

        public ObjectNode toJson() {
            ObjectNode result = super.toJson();
            if (!withExtendedSettings) {
                return result;
            }
            if (ecommerce != null) {
                result.set("ecommerce", BooleanNode.valueOf(BooleanUtils.toBoolean(ecommerce)));
            }
            if (dimension != null) {
                final JsonNode dimensionValue = IntNode.valueOf(Integer.parseInt(dimension));
                result.set("dimension", dimensionValue);
            }
            if (strategy != Strategy.NONE) {
                ObjectNode linker = JsonMapping.OM.createObjectNode();
                linker.set("strategy", TextNode.valueOf(strategy.toString()));
                final ArrayNode arrayNode = JsonMapping.OM.createArrayNode();
                Arrays.stream(domains.split(",")).map(String::strip).forEach(arrayNode::add);
                linker.set("domains", arrayNode);
                result.set("linker", linker);
            }
            return result;
        }

        public Strategy getStrategy() {
            return strategy;
        }

        @Getter
        @AllArgsConstructor
        public enum Strategy {
            @JsonProperty("none")
            @Description("Не использовать")
            NONE(false, "none"),
            @JsonProperty("ga")
            @Description("Скрипт analytics.js")
            SCRIPT_ANALYTICS_JS(true, "ga"),
            @JsonProperty("turbo")
            @Description("Measurement protocol")
            MEASUREMENT_PROTOCOL(true, "turbo");
            private final boolean domainsRequired;
            private final String name;

            @Override
            public String toString() {
                return name;
            }
        }
    }

    public static final class MailRuAnalyticsSettings extends AnalyticsSettings {

        public MailRuAnalyticsSettings(@JsonProperty("value") String value) {
            super(AnalyticsSystemType.MAILRU, value);
        }
    }

    public static final class RamblerAnalyticsSettings extends AnalyticsSettings {

        public RamblerAnalyticsSettings(@JsonProperty("value") String value) {
            super(AnalyticsSystemType.RAMBLER, value);
        }
    }

    public static final class MediascopeAnalyticsSettings extends AnalyticsSettings {

        public MediascopeAnalyticsSettings(@JsonProperty("value") String value) {
            super(AnalyticsSystemType.MEDIASCOPE, value);
        }
    }

    public static final class CustomAnalyticsSettings extends AnalyticsSettings {

        public CustomAnalyticsSettings(@JsonProperty("value") String value) {
            super(AnalyticsSystemType.CUSTOM, value);
        }

        public ObjectNode toJson() {
            ObjectNode result = JsonMapping.OM.createObjectNode();
            result.set("url", new TextNode(getValue()));
            result.set("type", new TextNode(getType().getDisplayName()));
            return result;
        }
    }
}
