package ru.yandex.direct.web.entity.uac.service

import org.springframework.stereotype.Service
import ru.yandex.direct.core.entity.campaign.service.validation.CampaignWithStrategyValidationUtils.getAutobudgetPayForConversionAvgCpaWarning
import ru.yandex.direct.core.entity.client.service.ClientService
import ru.yandex.direct.core.entity.feature.service.FeatureService
import ru.yandex.direct.core.entity.strategy.type.autobudgetcrr.CrrValidator
import ru.yandex.direct.core.entity.uac.model.UacStrategy
import ru.yandex.direct.core.entity.uac.model.UacStrategyData
import ru.yandex.direct.core.entity.uac.model.UacStrategyName
import ru.yandex.direct.dbutil.model.ClientId
import ru.yandex.direct.validation.builder.When
import ru.yandex.direct.validation.constraint.CommonConstraints.inSet
import ru.yandex.direct.validation.constraint.CommonConstraints.notNull
import ru.yandex.direct.validation.constraint.NumberConstraints.inRange
import ru.yandex.direct.validation.constraint.NumberConstraints.notGreaterThan
import ru.yandex.direct.validation.constraint.NumberConstraints.notLessThan
import ru.yandex.direct.validation.result.Defect
import ru.yandex.direct.validation.result.ValidationResult
import ru.yandex.direct.validation.util.property
import ru.yandex.direct.validation.util.validateObject
import ru.yandex.direct.validation.wrapper.DefaultValidator
import ru.yandex.direct.web.entity.uac.model.UacAvailableStrategyGoals
import java.math.BigDecimal
import kotlin.reflect.KProperty1


@Service
class RmpStrategyValidatorFactory(
    private val clientService: ClientService,
    private val featureService: FeatureService
) {
    fun createRmpStrategyValidator(clientId: ClientId, availableStrategies: UacAvailableStrategyGoals): RmpStrategyValidator {
        return RmpStrategyValidator(clientId, availableStrategies)
    }

    inner class RmpStrategyValidator internal constructor(
        private val clientId: ClientId,
        private val availableStrategies: UacAvailableStrategyGoals
    ) : DefaultValidator<UacStrategy> {
        private val currency = clientService.getWorkCurrency(clientId)
        override fun apply(t: UacStrategy): ValidationResult<UacStrategy, Defect<*>> {
            return validateObject(t) {
                property(UacStrategy::uacStrategyName) {
                    check(notNull())
                }
                property(UacStrategy::uacStrategyData) {
                    check(notNull())
                    checkBy(CrrValidator(),
                        When.isTrue(t.uacStrategyName == UacStrategyName.AUTOBUDGET_CRR))
                    checkBy(AvgCpiOrCpaValidator(UacStrategyData::avgCpa),
                        When.isTrue(t.uacStrategyName == UacStrategyName.AUTOBUDGET_AVG_CPA))
                    checkBy(AvgCpiOrCpaValidator(UacStrategyData::avgCpi),
                        When.isTrue(t.uacStrategyName == UacStrategyName.AUTOBUDGET_AVG_CPI))
                    checkBy(AvgClickValidator(),
                        When.isTrue(t.uacStrategyName == UacStrategyName.AUTOBUDGET_AVG_CLICK))
                }
            }
        }

        private inner class CrrValidator : DefaultValidator<UacStrategyData> {
            override fun apply(t: UacStrategyData): ValidationResult<UacStrategyData, Defect<*>> {
                return validateObject(t) {
                    property(UacStrategyData::crr) {
                        checkBy(CrrValidator, When.isValid())
                    }
                }
            }
        }

        private inner class AvgCpiOrCpaValidator(
            private val prop: KProperty1<UacStrategyData, BigDecimal?>
        ) : DefaultValidator<UacStrategyData> {

            override fun apply(t: UacStrategyData): ValidationResult<UacStrategyData, Defect<*>> {
                val autobudgetPayForConversionAvgCpaWarning = getAutobudgetPayForConversionAvgCpaWarning(
                    getAvailableFeatures(clientId), currency
                )
                return validateObject(t) {
                    property(prop) {
                        check(notNull())
                        check(notLessThan(currency.minAutobudgetAvgCpa))
                        check(notGreaterThan(autobudgetPayForConversionAvgCpaWarning))
                    }
                }
            }
        }

        private inner class AvgClickValidator: DefaultValidator<UacStrategyData> {
            override fun apply(t: UacStrategyData): ValidationResult<UacStrategyData, Defect<*>> {
                return validateObject(t) {
                    property(UacStrategyData::avgBid) {
                        check(notNull())
                        check(inRange(currency.minAutobudgetAvgPrice, currency.maxAutobudgetBid))
                    }
                }
            }
        }
    }

    private fun getAvailableFeatures(clientId: ClientId): Set<String> =
        featureService.getEnabledForClientId(clientId)
}
