package ru.yandex.direct.api.v5.entity.adgroups.validation.type

import org.springframework.stereotype.Component
import ru.yandex.direct.api.v5.entity.adgroups.AdGroupDefectTypes.feedsNotAllowed
import ru.yandex.direct.api.v5.entity.adgroups.AdGroupDefectTypes.hyperGeoSettingsWasFlushed
import ru.yandex.direct.api.v5.entity.adgroups.AdGroupDefectTypes.inconsistentAdGroupType
import ru.yandex.direct.api.v5.entity.adgroups.container.UpdateAdGroupsSimpleContainer
import ru.yandex.direct.api.v5.entity.ads.AdsDefectTypes.notSupportedAdGroupType
import ru.yandex.direct.api.v5.validation.DefectType
import ru.yandex.direct.api.v5.validation.DefectTypes.disableAllAutotargetingCategoriesIsForbidden
import ru.yandex.direct.core.entity.adgroup.container.AccessibleAdGroupTypes.API5_ALLOWED_AD_GROUP_TYPES
import ru.yandex.direct.core.entity.adgroup.model.AdGroup
import ru.yandex.direct.core.entity.adgroup.model.AdGroupType
import ru.yandex.direct.core.entity.adgroup.model.DynamicAdGroup
import ru.yandex.direct.core.entity.adgroup.model.TextAdGroup
import ru.yandex.direct.validation.builder.Constraint
import ru.yandex.direct.validation.builder.ItemValidationBuilder

@Component
class UpdateAdGroupsSimpleInternalRequestValidationTypeSupport : AdGroupsInternalRequestValidationTypeSupport<UpdateAdGroupsSimpleContainer> {
    override fun getTypeClass() = UpdateAdGroupsSimpleContainer::class.java

    override fun validate(items: List<UpdateAdGroupsSimpleContainer>) = items.map(this::validateItem)

    private fun validateItem(item: UpdateAdGroupsSimpleContainer) =
        ItemValidationBuilder.of<UpdateAdGroupsSimpleContainer, DefectType>(item)
            .check(allowedAdGroupTypeForUpdate())
            .check(allowedFilteredFeedPropertiesForAdGroupType())
            .check(dynamicAdGroupModelChangesHasNotEmptyRelevanceMatchCategories())
            .weakCheck(modelChangesHasFeedIsNotSet())
            .weakCheck(hyperGeoSettingIsNotFlushed())
            .result

    private fun allowedAdGroupTypeForUpdate(): Constraint<UpdateAdGroupsSimpleContainer, DefectType> {
        return Constraint.fromPredicate({ request ->
            request.oldAdGroup?.type?.let(API5_ALLOWED_AD_GROUP_TYPES::contains) ?: true
        }, notSupportedAdGroupType())
    }

    private fun allowedFilteredFeedPropertiesForAdGroupType(): Constraint<UpdateAdGroupsSimpleContainer, DefectType> {
        return Constraint.fromPredicate({ request ->
            val mc = request.modelChanges
            if (mc.isPropChanged(TextAdGroup.OLD_FEED_ID) || mc.isPropChanged(TextAdGroup.FEED_FILTER_CATEGORIES)) {
                request.oldAdGroup?.type == AdGroupType.BASE
            } else true
        }, inconsistentAdGroupType())
    }

    private fun dynamicAdGroupModelChangesHasNotEmptyRelevanceMatchCategories(): Constraint<UpdateAdGroupsSimpleContainer, DefectType> {
        return Constraint.fromPredicate({ request ->
            val mc = request.modelChanges
            if (DynamicAdGroup::class.java.isAssignableFrom(mc.modelType)) {
                val relevanceMatchCategories = mc.castModel(DynamicAdGroup::class.java)
                    .getPropIfChanged(DynamicAdGroup.RELEVANCE_MATCH_CATEGORIES)
                relevanceMatchCategories == null || relevanceMatchCategories.isNotEmpty()
            } else true
        }, disableAllAutotargetingCategoriesIsForbidden())
    }

    private fun modelChangesHasFeedIsNotSet(): Constraint<UpdateAdGroupsSimpleContainer, DefectType> {
        return Constraint.fromPredicate({ request ->
            val mc = request.modelChanges
            if (TextAdGroup::class.java.isAssignableFrom(mc.modelType)) {
                mc.castModel(TextAdGroup::class.java)
                    .getPropIfChanged(TextAdGroup.OLD_FEED_ID) == null
            } else true
        }, feedsNotAllowed())
    }

    private fun hyperGeoSettingIsNotFlushed(): Constraint<UpdateAdGroupsSimpleContainer, DefectType> {
        return Constraint.fromPredicate({ request ->
            request.oldAdGroup?.hyperGeoId == null || !request.modelChanges.isPropDeleted(AdGroup.HYPER_GEO_ID)
        }, hyperGeoSettingsWasFlushed())
    }
}
