package ru.yandex.canvas.service.video;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.assertj.core.util.Lists;

import ru.yandex.canvas.TimeDelta;
import ru.yandex.canvas.model.HtmlCharacterEscapes;
import ru.yandex.canvas.model.video.PythiaParams;
import ru.yandex.canvas.model.video.TrackingEvents;
import ru.yandex.canvas.model.video.addition.AdditionElement;
import ru.yandex.canvas.model.video.addition.Options;

import static java.lang.Math.floor;
import static ru.yandex.canvas.VideoConstants.PACKSHOT_START_NOTICE_URL;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class AdParams {

    @JsonProperty
    public static final String RTB_HOST_DC_PARAMS_MACRO = "${AUCTION_DC_PARAMS}";

    public static final String RTB_HOST_MEDIA_FILES_MACRO = "${MEDIA_FILES}";

    public static final String RTB_HOST_TRACKING_URL_MACRO = "${TRACKING_URL_PREFIX}";
    public static final String AUCTION_IMPRESSION_URL_MACRO = "${AUCTION_IMPRESSION_URL}";
    public static final String AUCTION_VIEW_NOTICES_MACRO = "${AUCTION_VIEW_NOTICES}";
    public static final String VIEW_NOTICES_MACRO_PREFIX = "VIEW_NOTICES_";
    public static final int VIEW_NOTICES_COUNT = 8;

    @JsonProperty("theme")
    private String theme;

    @JsonProperty("duration")
    private Double duration;

    @JsonRawValue
    @JsonProperty("mediaFiles")
    private String mediaFiles = RTB_HOST_MEDIA_FILES_MACRO;

    @JsonProperty("video_meta_id")
    private BigInteger videoMetaId;//идентификатор видео в VH CMS

    @JsonProperty("player_id")
    private String playerId;

    @JsonProperty("estKaltura")
    private String estKaltura;

    @JsonProperty("estMobileVertical")
    private String estMobileVertical;

    @JsonProperty("packshot_duration")
    private Double packshotDuration;

    @JsonProperty("PACKSHOT_START_NOTICE_URL")
    private String packshotStartNoticeUrl;

    @JsonProperty("PACKSHOT_IMAGE_URL")
    private String packshotImageUrl;

    @JsonProperty("HAS_ABUSE_BUTTON")
    private Boolean hasAbuseButton;

    @JsonProperty("socialAdvertising")
    private Boolean socialAdvertisement;

    @JsonRawValue
    @JsonProperty("AUCTION_DC_PARAMS")
    private String dcParams = RTB_HOST_DC_PARAMS_MACRO;

    @JsonProperty("playbackParameters")
    private PlaybackParameters playbackParameters;

    @JsonProperty("firstFrame")
    private FirstFrameParameters firstFrame;

    @JsonProperty("encounters")
    private List<String> encounters = Lists.newArrayList(RTB_HOST_TRACKING_URL_MACRO + "?action-id=14");

    @JsonProperty("pythia")
    private PythiaParams pythia;

    @JsonProperty("trackingEvents")
    private TrackingEvents trackingEvents;

    @JsonProperty("isStock")
    private Boolean isStock;

    @JsonIgnore
    private Long skipDelay;

    @JsonIgnore
    private Boolean showSkipButton = true;

    public static class PlaybackParameters {
        @JsonProperty("showSkipButton")
        Boolean showSkipButton = true;

        @JsonProperty("skipDelay")
        Long skipDelay;
    }

    public static class Images {
        @JsonProperty("type")
        String type = "image/jpeg";
        @JsonProperty("url")
        String url;
        @JsonProperty("width")
        Integer width;
        @JsonProperty("height")
        Integer height;
    }

    public static class FirstFrameParameters {
        @JsonProperty("images")
        List<Images> images;
    }

    @JsonUnwrapped
    @JsonSerialize(using = PcodeSerializer.class)
    private List<AdditionElement> elements;

    public static class PcodeSerializer extends JsonSerializer<List<AdditionElement>> {

        // Converts list of elements to hash like {PCODE-style dictonary like `{HAS_FOO=true, FOO_COLOR='#ffeedd'}}

        @Override
        public boolean isUnwrappingSerializer() {
            return true;
        }

        @Override
        public void serialize(List<AdditionElement> elements, JsonGenerator gen, SerializerProvider serializers)
                throws IOException {
            if (elements == null) {
                elements = Collections.emptyList();
            }

            Set<String> mandatoryElements = new HashSet<>(Arrays.asList("BODY", "TITLE", "DOMAIN", "AGE", "BUTTON"));

            for (AdditionElement element : elements) {
                Options options = element.getOptions();

                if (element.getType().getPcodeName() == null || !element.getAvailable()) {
                    continue;
                }

                gen.writeBooleanField("HAS_" + element.getType().getPcodeName(), element.getAvailable());

                options.serializeForPcode(gen);

                mandatoryElements.remove(element.getType().getPcodeName());
            }

            for (String key : mandatoryElements) {
                gen.writeBooleanField("HAS_" + key, false);
            }

        }
    }

    public String toJson() throws JsonProcessingException {

        if (packshotImageUrl != null) {
            packshotDuration = 3.0 + (floor(duration + 0.5) - duration);
            packshotStartNoticeUrl = PACKSHOT_START_NOTICE_URL;
        }

        playbackParameters = new PlaybackParameters();
        playbackParameters.skipDelay = skipDelay == null || skipDelay == 0 || (showSkipButton == null || !showSkipButton) ? null : skipDelay;
        playbackParameters.showSkipButton = skipDelay == null || showSkipButton == null ? false : showSkipButton;


        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.getFactory().setCharacterEscapes(new HtmlCharacterEscapes(false));

        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(this);
    }

    public AdParams setTheme(String theme) {
        this.theme = theme;
        return this;
    }

    public AdParams setDuration(Double duration) {
        this.duration = duration;
        return this;
    }

    public AdParams setDuration(TimeDelta duration) {
        this.duration = duration.asSeconds();
        return this;
    }

    public AdParams setVideoMetaId(BigInteger videoMetaId) {
        this.videoMetaId = videoMetaId;
        return this;
    }

    public AdParams setPlayerId(String playerId) {
        this.playerId = playerId;
        return this;
    }

    public AdParams setEstKaltura(String estKaltura) {
        this.estKaltura = estKaltura;
        return this;
    }

    public AdParams setEstMobileVertical(String estMobileVertical) {
        this.estMobileVertical = estMobileVertical;
        return this;
    }

    public AdParams setFirstFrame(FirstFrameParameters firstFrame) {
        this.firstFrame = firstFrame;
        return this;
    }

    public AdParams setElements(List<AdditionElement> elements) {
        this.elements = elements;
        return this;
    }

    public AdParams setPackshotImageUrl(String url) {
        this.packshotImageUrl = url;
        return this;
    }

    public AdParams setSkipDelay(TimeDelta skipOffset) {
        if (skipOffset == null) {
            this.skipDelay = null;
            return this;
        }
        this.skipDelay = (long) skipOffset.asSeconds();
        return this;
    }

    public AdParams setShowSkipButton(Boolean show) {
        this.showSkipButton = show;
        return this;
    }

    public AdParams setHasAbuseButton(Boolean hasAbuseButton) {
        this.hasAbuseButton = hasAbuseButton != null && hasAbuseButton ? null : false;
        return this;
    }

    public AdParams setPythia(PythiaParams pythia) {
        this.pythia = pythia;
        return this;
    }

    public AdParams setTrackingEvents(TrackingEvents trackingEvents) {
        this.trackingEvents = trackingEvents;
        return this;
    }

    public AdParams setIsStock(Boolean isStock) {
        this.isStock = isStock;
        return this;
    }

    public AdParams setSocialAdvertisement(Boolean socialAdvertisement) {
        this.socialAdvertisement = socialAdvertisement;
        return this;
    }

    public List<String> getEncounters() {
        return encounters;
    }
}
