package ru.yandex.dispenser.validation.providers.sqs;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import ru.yandex.dispenser.validation.client.CampaignType;
import ru.yandex.dispenser.validation.client.Environment;
import ru.yandex.dispenser.validation.client.ErrorCollection;
import ru.yandex.dispenser.validation.client.OrdersSubset;
import ru.yandex.dispenser.validation.client.Result;
import ru.yandex.dispenser.validation.client.model.Amount;
import ru.yandex.dispenser.validation.client.model.CreateChange;
import ru.yandex.dispenser.validation.client.model.CreateQuotaRequest;
import ru.yandex.dispenser.validation.client.model.QuotaRequest;
import ru.yandex.dispenser.validation.client.model.Units;
import ru.yandex.dispenser.validation.providers.CommonSettings;
import ru.yandex.dispenser.validation.providers.ProviderValidator;
import ru.yandex.dispenser.validation.providers.RequestedAmount;
import ru.yandex.dispenser.validation.providers.RequestedResource;

/**
 * SQS validator.
 *
 * @author Dmitriy Timashov <dm-tim@yandex-team.ru>
 */
@Component
public class SqsValidator implements ProviderValidator {

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

    private static final String PROVIDER_KEY = "sqs";

    private final CommonSettings commonSettings;
    private final List<RequestedResource> resources;

    public SqsValidator(CommonSettings commonSettings) {
        this.commonSettings = commonSettings;
        this.resources = List.of(new RequestedResource("write_capacity", Units.Count.COUNT));
    }

    @Override
    public CreateQuotaRequest prepareTestRequest(Environment environment, CampaignType campaignType) {
        CreateQuotaRequest.Builder builder = commonSettings.baseQuotaRequest(providerName());
        List<Long> orders = commonSettings.orders(campaignType, OrdersSubset.FULL);
        long currentValue = 1L;
        for (RequestedResource resource : resources) {
            for (Long orderId : orders) {
                long value = currentValue++;
                builder.addChange(CreateChange.builder()
                        .serviceKey(PROVIDER_KEY)
                        .orderId(orderId)
                        .resourceKey(resource.getKey())
                        .amount(Amount.builder()
                                .value(value)
                                .unit(resource.getUnit())
                                .build())
                        .build());
            }
        }
        return builder.build();
    }

    @Override
    public Result<String> validateTestRequest(QuotaRequest request, Environment environment,
                                              CampaignType campaignType) {
        Map<Long, Map<String, RequestedAmount>> requestedResources = new HashMap<>();
        request.getChanges().forEach(change ->
                requestedResources.computeIfAbsent(change.getOrder().getId(), k -> new HashMap<>())
                        .put(change.getResource().getKey(), new RequestedAmount(change.getAmount().getValue(),
                                Units.UNITS.get(change.getAmount().getUnit()))));
        Map<Long, Map<String, RequestedAmount>> baseResources = new HashMap<>();
        request.getBaseResourceChanges().forEach(change ->
                baseResources.computeIfAbsent(change.getBigOrder().getId(), k -> new HashMap<>())
                        .put(change.getBaseResource().getKey(), new RequestedAmount(change.getTotalAmount().getValue(),
                                Units.UNITS.get(change.getTotalAmount().getUnit()))));
        Map<Long, Map<String, RequestedAmount>> expectedBaseResources = new HashMap<>();
        requestedResources.forEach((orderId, resources) -> {
            RequestedAmount requestedWriteCapacity = resources.get("write_capacity");
            RequestedAmount baseWriteCapacity = new RequestedAmount(requestedWriteCapacity.getAmount(),
                    requestedWriteCapacity.getUnit());
            expectedBaseResources.computeIfAbsent(orderId, k -> new HashMap<>()).put("sqs:write_capacity",
                    baseWriteCapacity);
        });
        LOG.info("Expected {} base resources: {}", providerName(), expectedBaseResources);
        LOG.info("Actual {} base resources: {}", providerName(), baseResources);
        boolean match = baseResources.equals(expectedBaseResources);
        if (match) {
            return Result.success(providerName());
        } else {
            return Result.failure(ErrorCollection.builder().addError("Base resource mismatch for " + providerName())
                    .build());
        }
    }

    @Override
    public String providerName() {
        return "SQS";
    }

}
