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

import java.util.List;
import java.util.function.Function;

import javax.annotation.ParametersAreNonnullByDefault;

import com.google.common.collect.ImmutableList;
import com.yandex.direct.api.v5.dynamicfeedadtargets.SetBidsItem;
import com.yandex.direct.api.v5.dynamicfeedadtargets.SetBidsRequest;
import org.springframework.stereotype.Component;

import ru.yandex.direct.api.v5.validation.DefectType;
import ru.yandex.direct.validation.builder.ItemValidationBuilder;
import ru.yandex.direct.validation.builder.When;
import ru.yandex.direct.validation.result.ValidationResult;

import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.maxAdGroupsBidsPerRequest;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.maxCampBidsPerRequest;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.maxKeywordBidsPerRequest;
import static ru.yandex.direct.api.v5.validation.DefectTypes.mixedTypes;
import static ru.yandex.direct.api.v5.validation.SetBidsConstraints.bidsListSizeMaxLimit;
import static ru.yandex.direct.api.v5.validation.constraints.Constraints.eachNotNull;
import static ru.yandex.direct.validation.builder.Constraint.fromPredicate;

@Component
@ParametersAreNonnullByDefault
public class DynamicFeedAdTargetsSetBidsValidationService {
    private static final String BIDS_FIELD = SetBidsRequest.PropInfo.BIDS.propertyName;

    public static final int MAX_BID_IDS_PER_REQUEST = 10_000;
    public static final int MAX_BID_ADGROUP_IDS_PER_REQUEST = 1_000;
    public static final int MAX_BID_CAMPAIGN_IDS_PER_REQUEST = 100;

    private static final List<Function<SetBidsItem, Long>> ID_GETTERS = ImmutableList.of(
            SetBidsItem::getId, SetBidsItem::getAdGroupId, SetBidsItem::getCampaignId);

    public ValidationResult<SetBidsRequest, DefectType> validate(SetBidsRequest setBidsRequest) {
        ItemValidationBuilder<SetBidsRequest, DefectType> vb = ItemValidationBuilder.of(setBidsRequest);
        vb.list(setBidsRequest.getBids(), BIDS_FIELD)
                .check(eachNotNull())
                .check(bidsListSizeMaxLimit(SetBidsItem::getId, MAX_BID_IDS_PER_REQUEST,
                        t -> maxKeywordBidsPerRequest(),
                        SetBidsItem::getAdGroupId, MAX_BID_ADGROUP_IDS_PER_REQUEST,
                        t -> maxAdGroupsBidsPerRequest(),
                        SetBidsItem::getCampaignId, MAX_BID_CAMPAIGN_IDS_PER_REQUEST,
                        t -> maxCampBidsPerRequest()), When.isValid())
                .check(fromPredicate(this::allItemsHaveIdsOfTheSameType, mixedTypes()), When.isValid());
        return vb.getResult();
    }

    private boolean allItemsHaveIdsOfTheSameType(List<SetBidsItem> items) {
        return ID_GETTERS.stream()
                .anyMatch(getter -> items.stream().allMatch(t -> itemHasSpecifiedId(t, getter)));
    }

    private boolean itemHasSpecifiedId(SetBidsItem item, Function<SetBidsItem, Long> getter) {
        return getter.apply(item) != null;
    }
}
