package ru.yandex.direct.api.v5.entity.campaignsext.validation

import com.yandex.direct.api.v5.campaignsext.CampaignUpdateItem
import com.yandex.direct.api.v5.campaignsext.UpdateRequest
import org.springframework.stereotype.Component
import ru.yandex.direct.api.v5.entity.campaigns.CampaignDefectTypes
import ru.yandex.direct.api.v5.entity.campaigns.CampaignDefectTypes.campaignTypeNotSupported
import ru.yandex.direct.api.v5.entity.campaignsext.container.UpdateCampaignsExtContainer
import ru.yandex.direct.api.v5.entity.campaignsext.converter.CampaignsExtUpdateRequestConverter.Companion.convertCampaignTypeToExternal
import ru.yandex.direct.api.v5.validation.DefectType
import ru.yandex.direct.api.v5.validation.DefectTypes
import ru.yandex.direct.api.v5.validation.validateList
import ru.yandex.direct.api.v5.validation.validateObject
import ru.yandex.direct.core.entity.campaign.AvailableCampaignSources.isSupportedInAPI5
import ru.yandex.direct.core.entity.campaign.model.CampaignType
import ru.yandex.direct.core.entity.campaign.model.CampaignTypeKinds
import ru.yandex.direct.core.entity.campaign.model.CampaignWithCampaignType
import ru.yandex.direct.core.entity.campaign.model.CampaignWithSource
import ru.yandex.direct.validation.builder.Constraint
import ru.yandex.direct.validation.result.ValidationResult

@Component
class CampaignsExtUpdateRequestValidator {

    fun validateExternalRequest(request: UpdateRequest) =
        validateObject(request) {
            item(request.campaigns, "campaigns")
                .check(campaignsCountNotMoreThanMax())
        }.takeIf { it.hasAnyErrors() }

    /**
     * Валидация внутреннего запроса происходит в
     * [ru.yandex.direct.api.v5.entity.campaignsext.converter.CampaignsExtUpdateRequestConverter]
     *
     * Этот метод не валидирует запрос, а просто возвращает уже найденные ошибки и предупреждения
     */
    fun validateInternalRequest(request: List<UpdateCampaignsExtContainer<*>>) =
        validateList(request) {
            checkEachBy { _, item ->
                ValidationResult(
                    item,
                    item.errors,
                    item.warnings,
                )
            }
        }

    companion object SimpleValidations {

        private val API5_EDIT_CAMPAIGNS_EXT_STRING = CampaignTypeKinds.API5_EDIT_CAMPAIGNS_EXT
            .joinToString()

        private const val UPDATE_IDS_LIMIT = 10

        fun campaignsCountNotMoreThanMax() = Constraint<List<CampaignUpdateItem>, DefectType> { campaigns ->
            if (campaigns.size > UPDATE_IDS_LIMIT) {
                CampaignDefectTypes.maxCampaignsPerUpdateRequest(UPDATE_IDS_LIMIT)
            } else {
                null
            }
        }

        fun allowedCampaignType(
            campaignWithType: CampaignWithCampaignType?,
            allowedCampaignTypes: Set<CampaignType>,
            defectType: DefectType
        ): Constraint<CampaignUpdateItem, DefectType> = Constraint.fromPredicateOfNullable(
            { campaignWithType != null && allowedCampaignTypes.contains(campaignWithType.type) },
            defectType
        )

        fun allowedCampaignSource(campaignWithSource: CampaignWithSource?): Constraint<CampaignUpdateItem, DefectType> =
            Constraint.fromPredicate(
                { campaignWithSource == null || isSupportedInAPI5(campaignWithSource.source) },
                campaignTypeNotSupported()
            )

        /**
         * Аналог перловой проверки https://a.yandex-team.ru/arc_vcs/direct/perl/api/services/v5/API/Service/Campaigns.pm?blame=true&rev=r8950186#L868-890
         */
        fun validCampaignType() = Constraint<CampaignUpdateItem, DefectType> { item ->
            val structures = listOfNotNull(
                item.textCampaign,
                item.cpmBannerCampaign,
                item.dynamicTextCampaign,
                item.mobileAppCampaign,
                item.smartCampaign,
                item.contentPromotionCampaign,
            )

            if (structures.count() > 1) {
                DefectTypes.possibleOnlyOneField(API5_EDIT_CAMPAIGNS_EXT_STRING)
            } else {
                null
            }
        }

        fun campaignTypeNotMatchesWithTypeInRequest(
            typeFromDb: CampaignType?,
            typeFromRequest: CampaignType?
        ): DefectType? =
            if (typeFromDb != null && typeFromRequest != null && typeFromDb != typeFromRequest)
                CampaignDefectTypes.campaignTypeNotMatchesWithTypeInRequest(
                    convertCampaignTypeToExternal(typeFromDb)!!.name,
                    convertCampaignTypeToExternal(typeFromRequest)!!.name
                )
            else null

        fun campaignIdShouldBePositive(item: CampaignUpdateItem): DefectType? =
            if (item.id <= 0) DefectTypes.fieldShouldBePositive("Id") else null

    }
}