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

import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import ru.yandex.qe.dispenser.api.util.EnumUtils;
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.QuotaChangeOwningCostTariffManager;

@Component
public class RtmrProcessingOwningCostFormula implements ProviderOwningCostFormula {

    private static final Logger LOG = LoggerFactory.getLogger(RtmrProcessingOwningCostFormula.class);

    private static final Provider PROVIDER = Provider.RTMR_PROCESSING;
    private static final BigDecimal CPU_PRICE = new BigDecimal("769.03", MATH_CONTEXT);
    private static final BigDecimal DATA_FLOW_PRICE = new BigDecimal("100.05", MATH_CONTEXT);
    private static final BigDecimal YF_SLOT_PRICE = new BigDecimal("605.43", MATH_CONTEXT);

    public RtmrProcessingOwningCostFormula() {
    }

    @Override
    public @NotNull String getProviderKey() {
        return PROVIDER.getServiceKey();
    }

    public @NotNull Map<QuotaChangeRequest.ChangeKey, BigDecimal> calculateOwningCostFromContext(
            @NotNull Collection<ChangeOwningCostContext> changes) {
        Map<QuotaChangeRequest.ChangeKey, BigDecimal> result = new HashMap<>();
        Map<CampaignKey, List<ChangeOwningCostContext>> changesByCampaign = ProviderOwningCostFormula
                .groupByCampaign(changes, result);
        changesByCampaign.forEach((campaignKey, campaignChanges) -> {
            if (QuotaChangeOwningCostTariffManager.CAMPAIGN_KEYS_2022.contains(campaignKey.getKey())) {
                // No RTMR processing resources in 2022 campaign
                campaignChanges.forEach(change -> result.put(change.getChange().getKey(), DEFAULT_OWNING_COST));
            } else {
                Map<QuotaChangeRequest.ChangeKey, BigDecimal> campaignCosts = campaignChanges.stream()
                        .map(ChangeOwningCostContext::getChange)
                        .collect(Collectors.toMap(QuotaChangeRequest.ChangeAmount::getKey, this::toOwningCost));
                result.putAll(campaignCosts);
            }
        });
        return result;
    }

    private BigDecimal toOwningCost(QuotaChangeRequest.Change change) {
        BigDecimal result = DEFAULT_OWNING_COST;
        String publicKey = change.getResource().getPublicKey();
        Optional<Resource> resourcesO = Resource.byKey(publicKey);
        if (resourcesO.isPresent()) {
            switch (resourcesO.get()) {
                case CPU:
                    result = calculateForCpu(change);
                    break;
                case DATA_FLOW:
                    result = calculateForDataFlow(change);
                    break;
                case YF_SLOT:
                    result = calculateForYfSlot(change);
                    break;
                case DATA:
                    break;
            }
        }
        return result;
    }

    private BigDecimal calculateForCpu(QuotaChangeRequest.Change change) {
        return convert(change, DiUnit.CORES, LOG).multiply(CPU_PRICE, MATH_CONTEXT);
    }

    private BigDecimal calculateForDataFlow(QuotaChangeRequest.Change change) {
        return convert(change, DiUnit.MIBPS, LOG).multiply(DATA_FLOW_PRICE, MATH_CONTEXT);
    }

    private BigDecimal calculateForYfSlot(QuotaChangeRequest.Change change) {
        return convert(change, DiUnit.COUNT, LOG).multiply(YF_SLOT_PRICE, MATH_CONTEXT);
    }

    private enum Resource implements EnumUtils.StringKey {
        CPU("cpu"),
        DATA_FLOW("dataflow"),
        DATA("data"),
        YF_SLOT("yf_slot"),
        ;

        private static Map<String, Resource> resourceByKey;
        private final String key;

        Resource(String key) {
            this.key = key;
        }

        public String getKey() {
            return key;
        }

        public static Optional<Resource> byKey(String key) {
            if (Resource.resourceByKey == null) {
                Resource.resourceByKey = ImmutableMap.copyOf(EnumUtils.prepareKeysMap(Resource.values()));
            }
            return Optional.ofNullable(Resource.resourceByKey.get(key));
        }
    }

}
