package ru.yandex.qe.dispenser.ws.quota.request.owning_cost.formula

import mu.KotlinLogging
import org.springframework.stereotype.Component
import ru.yandex.qe.dispenser.api.v1.DiUnit
import ru.yandex.qe.dispenser.domain.QuotaChangeRequest
import ru.yandex.qe.dispenser.ws.bot.Provider
import ru.yandex.qe.dispenser.ws.quota.request.owning_cost.ChangeOwningCostContext
import ru.yandex.qe.dispenser.ws.quota.request.owning_cost.pricing.PricingModel
import ru.yandex.qe.dispenser.ws.quota.request.owning_cost.pricing.QuotaChangeOwningCostTariffManager
import java.math.BigDecimal

private val logger = KotlinLogging.logger {}

@Component
class S3OwningCostFormula(
    private val tariffManager: QuotaChangeOwningCostTariffManager
): ProviderOwningCostFormula {

    private val provider: Provider = Provider.S3
    private val hoursPerMonth: BigDecimal = BigDecimal.valueOf(720L)
    private val x2HddSku = "s3.quota_space.hdd.x2"
    private val x3HddSku = "s3.quota_space.hdd.x3"

    override fun getProviderKey(): String {
        return provider.serviceKey
    }

    override fun calculateOwningCostFromContext(
        changes: MutableCollection<ChangeOwningCostContext>
    ): MutableMap<QuotaChangeRequest.ChangeKey, BigDecimal> {
        val result = mutableMapOf<QuotaChangeRequest.ChangeKey, BigDecimal>()
        val changesByCampaign = ProviderOwningCostFormula.groupByCampaign(changes, result)
        changesByCampaign.forEach { (campaignKey, campaignChanges) ->
            val pricingBySKU = tariffManager.getByProviderCampaign(provider, campaignKey.key).associateBy { it.sku }
            if (pricingBySKU.isNotEmpty()) {
                result.putAll(campaignChanges.associateBy { it.change.key }.mapValues { toOwningCost(it.value, pricingBySKU) })
            }
        }
        return result
    }

    private fun toOwningCost(change: ChangeOwningCostContext, pricingBySKU: Map<String, PricingModel>): BigDecimal {
        return when (ProviderOwningCostFormula.CampaignKey.byKey(change.campaign.key).orElse(null)) {
            ProviderOwningCostFormula.CampaignKey.AUG_2022_DRAFT, ProviderOwningCostFormula.CampaignKey.AUG_2022_AGGREGATED -> toOwningCost2022(change, pricingBySKU)
            else -> ProviderOwningCostFormula.DEFAULT_OWNING_COST
        }
    }

    private fun toOwningCost2022(change: ChangeOwningCostContext, pricingBySKU: Map<String, PricingModel>): BigDecimal {
        return when (Resource.byKey(change.change.resource.publicKey)) {
            Resource.X2_HDD -> cost(x2HddSku, change, pricingBySKU)
            Resource.X3_HDD -> cost(x3HddSku, change, pricingBySKU)
            else -> ProviderOwningCostFormula.DEFAULT_OWNING_COST
        }
    }

    private fun cost(sku: String, change: ChangeOwningCostContext, pricingBySKU: Map<String, PricingModel>): BigDecimal {
        val pricing = pricingBySKU[sku] ?: return ProviderOwningCostFormula.DEFAULT_OWNING_COST
        return convert(change.change, DiUnit.GIBIBYTE, logger)
            .multiply(hoursPerMonth, ProviderOwningCostFormula.MATH_CONTEXT)
            .multiply(pricing.price, ProviderOwningCostFormula.MATH_CONTEXT)
    }

    private enum class Resource(private val key: String) {
        X2_HDD("x2_hdd"),
        X3_HDD("x3_hdd");

        companion object {
            private val resourceByKey: Map<String, Resource> = values().associateBy { it.key }

            fun byKey(key: String): Resource? {
                return resourceByKey[key]
            }
        }
    }

}
