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

import ru.yandex.direct.api.v5.common.validation.DefaultApiPresentations;
import ru.yandex.direct.api.v5.common.validation.DefectPresentationsHolder;
import ru.yandex.direct.api.v5.validation.ApiDefectIds;
import ru.yandex.direct.api.v5.validation.DefectTypes;
import ru.yandex.direct.core.entity.bids.container.ExternalFieldsBidsMapping;
import ru.yandex.direct.core.entity.bids.container.SetAutoBidItem;
import ru.yandex.direct.core.entity.bids.container.SetAutoNetworkByCoverage;
import ru.yandex.direct.core.entity.bids.container.SetAutoSearchByPosition;
import ru.yandex.direct.core.entity.bids.container.SetBidItem;
import ru.yandex.direct.core.entity.bids.validation.BidsDefects;
import ru.yandex.direct.i18n.types.Identity;
import ru.yandex.direct.validation.defect.ids.CollectionDefectIds;
import ru.yandex.direct.validation.defect.ids.NumberDefectIds;

import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidChangeNotAllowedContextDependsOnSearch;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidChangeNotAllowedContextIsSwitchedOff;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidChangeNotAllowedForAutobudgetStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidChangeNotAllowedSearchIsSwitchedOff;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidMustBeNotGreaterThan;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidMustBeNotLessThan;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidWontBeAccepted;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidWontBeAcceptedContextDependsOnSearch;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidWontBeAcceptedContextIsSwitchedOff;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.bidWontBeAcceptedSearchIsSwitchedOff;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.contextPriceIsNotSetForManualStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.dependentFieldMissing;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.fieldDoesNotMatchStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.fieldRequiredForContextStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.fieldRequiredForSearchStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.keywordNotFound;
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.entity.bids.validation.BidsDefectTypes.notEligibleObjectRelevanceMatchCantBeUsedInPriceConstructor;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.oneOfFieldsRequiredDetailed;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.priorityWontBeAcceptedNotAutoBudgetStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.requiredAtLeastOneOfFields;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.requiredAtLeastOneOfFieldsForAutobudgetStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.requiredAtLeastOneOfFieldsForManualStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.searchPriceIsNotSetForManualStrategy;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.strategyIsNotSet;
import static ru.yandex.direct.api.v5.entity.bids.validation.BidsDefectTypes.strategyPriorityIsNotSetForAutobudgetStrategy;
import static ru.yandex.direct.api.v5.validation.DefectTypes.badStatusCampaignArchived;
import static ru.yandex.direct.api.v5.validation.DefectTypes.invalidValue;
import static ru.yandex.direct.api.v5.validation.DefectTypes.mixedTypes;
import static ru.yandex.direct.api.v5.validation.DefectTypes.possibleOnlyOneField;
import static ru.yandex.direct.api.v5.validation.DefectTypes.translations;

/**
 * DefectPresentations переопределённые для сервиса Bids.
 * Fallback на {@link DefaultApiPresentations#HOLDER}
 */
public class BidsDefectPresentations {
    public static final ExternalFieldsBidsMapping BIDS_IDS_MAPPING =
            ExternalFieldsBidsMapping.builder()
                    .map(SetBidItem.ID, "KeywordId")
                    .map(SetBidItem.AD_GROUP_ID, "AdGroupId")
                    .map(SetBidItem.CAMPAIGN_ID, "CampaignId")
                    .map(SetBidItem.PRICE_SEARCH, "Bid")
                    .map(SetBidItem.PRICE_CONTEXT, "ContextBid")
                    .map(SetBidItem.AUTOBUDGET_PRIORITY, "StrategyPriority")
                    .map(SetAutoBidItem.ID, "KeywordId")
                    .map(SetAutoBidItem.AD_GROUP_ID, "AdGroupId")
                    .map(SetAutoBidItem.CAMPAIGN_ID, "CampaignId")
                    // поля searchByTrafficVolume, searchByPosition и networkByCoverage не мапятся на поля запроса Bids.setAuto
                    .map(SetAutoBidItem.SEARCH_BY_TRAFFIC_VOLUME, "")
                    .map(SetAutoBidItem.SEARCH_BY_POSITION, "")
                    .map(SetAutoBidItem.NETWORK_BY_COVERAGE, "")
                    .map(SetAutoSearchByPosition.POSITION, "Position")
                    .map(SetAutoSearchByPosition.INCREASE_PERCENT, "IncreasePercent")
                    .map(SetAutoSearchByPosition.CALCULATED_BY, "CalculateBy")
                    .map(SetAutoSearchByPosition.MAX_BID, "MaxBid")
                    .map(SetAutoNetworkByCoverage.INCREASE_PERCENT, "IncreasePercent")
                    .map(SetAutoNetworkByCoverage.CONTEXT_COVERAGE, "ContextCoverage")
                    .map(SetAutoNetworkByCoverage.MAX_BID, "MaxBid")
                    .map(SetAutoBidItem.SCOPE, "Scope")
                    .build();

    public static final DefectPresentationsHolder HOLDER = DefectPresentationsHolder
            .builderWithFallback(DefaultApiPresentations.HOLDER)
            // default overrides
            .register(CollectionDefectIds.Gen.MUST_NOT_CONTAIN_DUPLICATED_ELEMENTS,
                    t -> DefectTypes.duplicatedElement().withDetailedMessage(m -> ""))
            // BidsDefects.CollectionIds
            .register(ApiDefectIds.CollectionIds.MAX_SHOW_CONDITIONS_PER_REQUEST,
                    t -> maxKeywordBidsPerRequest())
            .register(ApiDefectIds.CollectionIds.MAX_AD_GROUP_IDS_PER_REQUEST,
                    t -> maxAdGroupsBidsPerRequest())
            .register(ApiDefectIds.CollectionIds.MAX_CAMPAIGN_IDS_PER_REQUEST,
                    t -> maxCampBidsPerRequest())
            // BidsDefects.Bids
            .register(BidsDefects.Bids.NOT_FOUND_SHOW_CONDITION_BY_PARAMETERS,
                    t -> keywordNotFound(BIDS_IDS_MAPPING.getBidsField(t.getField()), t.getId()))
            .register(BidsDefects.Bids.BAD_STATUS_CAMPAIGN_ARCHIVED_ON_UPDATE_BIDS,
                    t -> badStatusCampaignArchived())
            .register(BidsDefects.Bids.RELEVANCE_MATCH_CANT_BE_USED_IN_SET_AUTO,
                    t -> notEligibleObjectRelevanceMatchCantBeUsedInPriceConstructor(t.getId()))
            // BidsDefects.ModelDefects
            .register(BidsDefects.ModelDefects.REQUIRED_AT_LEAST_ONE_OF_FIELDS,
                    t -> requiredAtLeastOneOfFields(BIDS_IDS_MAPPING.getBidsFields(t)))
            .register(BidsDefects.ModelDefects.REQUIRED_AT_LEAST_ONE_OF_FIELDS_FOR_MANUAL_STRATEGY,
                    t -> requiredAtLeastOneOfFieldsForManualStrategy(BIDS_IDS_MAPPING.getBidsFields(t)))
            .register(BidsDefects.ModelDefects.REQUIRED_AT_LEAST_ONE_OF_FIELDS_FOR_AUTOBUDGET_STRATEGY,
                    t -> requiredAtLeastOneOfFieldsForAutobudgetStrategy(BIDS_IDS_MAPPING.getBidsFields(t)))
            .register(BidsDefects.ModelDefects.ONE_OF_FIELDS_SHOULD_BE_SPECIFIED,
                    t -> oneOfFieldsRequiredDetailed(BIDS_IDS_MAPPING.getBidsFields(t)))
            .register(BidsDefects.ModelDefects.POSSIBLE_ONLY_ONE_FIELD,
                    t -> possibleOnlyOneField(BIDS_IDS_MAPPING.getBidsFields(t)))
            .register(BidsDefects.Bids.FIELD_DOES_NOT_MATCH_STRATEGY,
                    t -> fieldDoesNotMatchStrategy(BIDS_IDS_MAPPING.getBidsField(t.getField())))
            .register(BidsDefects.Bids.FIELD_REQUIRED_FOR_SEARCH_STRATEGY,
                    t -> fieldRequiredForSearchStrategy(BIDS_IDS_MAPPING.getBidsField(t.getField())))
            .register(BidsDefects.Bids.FIELD_REQUIRED_FOR_CONTEXT_STRATEGY,
                    t -> fieldRequiredForContextStrategy(BIDS_IDS_MAPPING.getBidsField(t.getField())))
            .register(BidsDefects.BidsDefects2Fields.DEPENDENT_FIELD_MISSING,
                    t -> dependentFieldMissing(BIDS_IDS_MAPPING.getBidsField(t.getField()),
                            BIDS_IDS_MAPPING.getBidsField(t.getField2())))
            // BidsDefects.Ids
            .register(BidsDefects.Ids.MIXED_TYPES,
                    t -> mixedTypes().withDetailedMessage(m -> ""))
            .register(BidsDefects.Ids.STRATEGY_IS_NOT_SET,
                    t -> strategyIsNotSet())
            .register(BidsDefects.Ids.CONTEXT_PRICE_IS_NOT_SET_FOR_MANUAL_STRATEGY,
                    t -> contextPriceIsNotSetForManualStrategy())
            .register(BidsDefects.Ids.SEARCH_PRICE_IS_NOT_SET_FOR_MANUAL_STRATEGY,
                    t -> searchPriceIsNotSetForManualStrategy())
            .register(BidsDefects.Ids.PRIORITY_IS_NOT_SET_FOR_AUTO_STRATEGY,
                    t -> strategyPriorityIsNotSetForAutobudgetStrategy())
            .register(BidsDefects.Ids.BID_FOR_SEARCH_WONT_BE_ACCEPTED_IN_CASE_OF_AUTOBUDGET_STRATEGY,
                    t -> BidsDefectTypes.bidWontBeAcceptedInCaseOfAutoBudgetStrategy())
            .register(BidsDefects.Ids.BID_FOR_CONTEXT_WONT_BE_ACCEPTED_IN_CASE_OF_AUTOBUDGET_STRATEGY,
                    t -> BidsDefectTypes.bidWontBeAcceptedInCaseOfAutoBudgetStrategy())
            .register(BidsDefects.Ids.PRIORITY_WONT_BE_ACCEPTED_IN_CASE_OF_NOT_AUTO_BUDGET_STRATEGY,
                    t -> priorityWontBeAcceptedNotAutoBudgetStrategy())
            .register(BidsDefects.Ids.BID_NOT_ALLOWED_FOR_AUTOBUDGET_STRATEGY,
                    t -> bidChangeNotAllowedForAutobudgetStrategy())
            .register(BidsDefects.Ids.BID_NOT_ALLOWED_FOR_BS_RARELY_LOADED_ADGROUP,
                    t -> bidWontBeAccepted())
            .register(BidsDefects.Ids.BID_FOR_SEARCH_NOT_ALLOWED_SEARCH_IS_SWITCHED_OFF,
                    t -> bidChangeNotAllowedSearchIsSwitchedOff())
            .register(BidsDefects.Ids.BID_FOR_CONTEXT_NOT_ALLOWED_NET_IS_SWITCHED_OFF,
                    t -> bidChangeNotAllowedContextIsSwitchedOff())
            .register(BidsDefects.Ids.BID_FOR_CONTEXT_NOT_ALLOWED_NOT_DIFFERENT_PLACES,
                    t -> bidChangeNotAllowedContextDependsOnSearch())
            .register(BidsDefects.Ids.BID_FOR_SEARCH_WONT_BE_ACCEPTED_SEARCH_IS_SWITCHED_OFF,
                    t -> bidWontBeAcceptedSearchIsSwitchedOff())
            .register(BidsDefects.Ids.BID_FOR_CONTEXT_WONT_BE_ACCEPTED_NET_IS_SWITCHED_OFF,
                    t -> bidWontBeAcceptedContextIsSwitchedOff())
            .register(BidsDefects.Ids.BID_FOR_CONTEXT_WONT_BE_ACCEPTED_NOT_DIFFERENT_PLACES,
                    t -> bidWontBeAcceptedContextDependsOnSearch())
            // BidsDefects.CurrencyAmountDefects
            .register(BidsDefects.CurrencyAmountDefects.SEARCH_PRICE_IS_NOT_GREATER_THAN_MIN,
                    t -> bidMustBeNotLessThan(t.getMoneyValue().getCurrencyCode().getTranslation().longForm(),
                            Identity.of(t.getMoneyValue().micros())))
            .register(BidsDefects.CurrencyAmountDefects.SEARCH_PRICE_IS_NOT_SMALLER_THAN_MAX,
                    t -> bidMustBeNotGreaterThan(t.getMoneyValue().getCurrencyCode().getTranslation().longForm(),
                            Identity.of(t.getMoneyValue().micros())))
            .register(BidsDefects.CurrencyAmountDefects.CPM_PRICE_IS_NOT_GREATER_THAN_MIN,
                    t -> bidMustBeNotLessThan(t.getMoneyValue().getCurrencyCode().getTranslation().longForm(),
                            Identity.of(t.getMoneyValue().micros())))
            .register(BidsDefects.CurrencyAmountDefects.CPM_PRICE_IS_NOT_SMALLER_THAN_MAX,
                    t -> bidMustBeNotGreaterThan(t.getMoneyValue().getCurrencyCode().getTranslation().longForm(),
                            Identity.of(t.getMoneyValue().micros())))
            .register(NumberDefectIds.MUST_BE_IN_THE_INTERVAL_INCLUSIVE,
                    t -> invalidValue().withDetailedMessage(
                            (path, value) -> translations()
                                    .fieldMustBeInRange(path, Identity.of(t.getMin().intValue()),
                                            Identity.of(t.getMax().intValue()))
                    ))
            .register(NumberDefectIds.MUST_BE_NOT_OLDER, t -> invalidValue())
            .build();
}
