package ru.yandex.direct.api.v5.entity.ads.validation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.ParametersAreNonnullByDefault;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;

import ru.yandex.direct.core.entity.banner.model.BannerWithAdGroupId;
import ru.yandex.direct.core.entity.banner.model.BannerWithSystemFields;
import ru.yandex.direct.core.entity.banner.model.StubBanner;
import ru.yandex.direct.model.ModelChanges;

/**
 * Фабричные методы для инстанцирования специальных (сигнальных) объектов –
 * способ передать информацию об ошибках
 * с первого (externalRequest) на второй (internalRequest) этап валидации.
 */
@ParametersAreNonnullByDefault
public class AdsApiValidationSignals {

    private AdsApiValidationSignals() {
        // no instantiation
    }

    /**
     * @return {@link BannerWithVideoExtensionIdNotSpecified}
     */
    public static BannerWithAdGroupId bannerWithVideoExtensionIdNotSpecified() {
        return new BannerWithVideoExtensionIdNotSpecified();
    }

    /**
     * @return {@link BannerWithBannerTypeNotSpecified}
     */
    public static BannerWithAdGroupId bannerWithBannerTypeNotSpecified() {
        return new BannerWithBannerTypeNotSpecified();
    }

    public static ModelChanges<BannerWithSystemFields> bannerChangesWithBannerTypeNotSpecified() {
        return new ModelChanges<>(-1L, AdsApiValidationSignals.BannerWithBannerTypeNotSpecified.class)
                .castModelUp(BannerWithSystemFields.class);
    }

    /**
     * @return {@link BannerWithAmbiguousType}
     */
    public static BannerWithAdGroupId bannerWithAmbiguousType() {
        return new BannerWithAmbiguousType();
    }

    public static ModelChanges<BannerWithSystemFields> bannerChangesWithAmbiguousType() {
        return new ModelChanges<>(-1L, AdsApiValidationSignals.BannerWithAmbiguousType.class)
                .castModelUp(BannerWithSystemFields.class);
    }

    static boolean hasVideoExtensionIdSpecified(BannerWithAdGroupId banner) {
        return !(banner instanceof BannerWithVideoExtensionIdNotSpecified);
    }

    static boolean hasBannerTypeSpecified(BannerWithAdGroupId banner) {
        return !(banner instanceof BannerWithBannerTypeNotSpecified);
    }

    static boolean hasUnambiguousType(BannerWithAdGroupId banner) {
        return !(banner instanceof BannerWithAmbiguousType);
    }

    public static class Callouts {
        public static final List<Long> LIST_TOO_LONG = ImmutableList.of(-300300L);
        public static final List<Long> INCOMPATIBLE_OPERATIONS = ImmutableList.of(-101L);

        private Callouts() {
            // no instantiation
        }

        public enum Defect {
            /**
             * Коллаут уже привязан к объявлению, нельзя добавить.
             */
            ALREADY_LINKED,
            /**
             * Коллаут не привязан к объявлению, нельзя удалить.
             */
            NOT_LINKED,
            /**
             * Более одной операции над одним элементом.
             */
            DUPLICATE,
            /**
             * Неположительный id.
             */
            INVALID_ID,
        }

        public static class DefectsContainer extends ArrayList<Long> {
            /**
             * Маппим значение на все индексы (общий случай – есть дубликаты), по которым
             * оно встречалось в исходном запросе.
             */
            private final Multimap<Long, Integer> originalIndices = ArrayListMultimap.create();
            private final Map<Long, Defect> defects = new LinkedHashMap<>();

            public DefectsContainer(Collection<? extends Long> c) {
                super(c);
            }

            public void addDefect(int originalIndex, Long id, Defect defect) {
                defects.put(id, defect);
                originalIndices.put(id, originalIndex);
            }

            public boolean hasDefects() {
                return !defects.isEmpty();
            }

            public Map<Long, Defect> getDefects() {
                return ImmutableMap.copyOf(defects);
            }

            public Collection<Integer> getOriginalIndices(Long id) {
                return originalIndices.get(id);
            }
        }
    }

    /**
     * Баннер, при добавлении/обновлении которого был указан блок
     * {@code VideoExtensionItem}, но в нём отсутствовал id.
     */
    private static class BannerWithVideoExtensionIdNotSpecified extends InvalidBanner {
    }

    /**
     * В запросе не было указано ни одного элемента типа.
     */
    @SuppressWarnings("WeakerAccess")
    public static class BannerWithBannerTypeNotSpecified extends InvalidBanner {
    }

    /**
     * В запросе было указано более одного типа для баннера.
     */
    @SuppressWarnings("WeakerAccess")
    public static class BannerWithAmbiguousType extends InvalidBanner {
    }

    private static class InvalidBanner extends StubBanner {
        InvalidBanner() {
            setId(-1L);
        }
    }
}
