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

import java.math.BigInteger;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ru.yandex.bolts.collection.Tuple2;
import ru.yandex.qe.dispenser.domain.QuotaChangeRequest;
import ru.yandex.qe.dispenser.domain.dao.quota.request.QuotaChangeRequestDao;

/**
 * Transaction wrapper.
 *
 * @author Ruslan Kadriev <aqru@yandex-team.ru>
 */
@Component
public class CampaignOwningCostRefreshTransactionWrapper {
    private static final Logger LOG = LoggerFactory.getLogger(CampaignOwningCostRefreshTransactionWrapper.class);
    private static final int MAX_REPEATS = 10000;
    // DISPENSER-4758
    public static final Set<QuotaChangeRequest.Status> VALID_STATUSES = EnumSet.of(
            QuotaChangeRequest.Status.NEW,
            QuotaChangeRequest.Status.CONFIRMED,
            QuotaChangeRequest.Status.READY_FOR_REVIEW,
            QuotaChangeRequest.Status.COMPLETED,
            QuotaChangeRequest.Status.NEED_INFO,
            QuotaChangeRequest.Status.APPROVED
    );
    public static final long LIMIT = 100L;
    public static final String KEY = "";
    private final QuotaChangeRequestDao quotaChangeRequestDao;

    public CampaignOwningCostRefreshTransactionWrapper(QuotaChangeRequestDao quotaChangeRequestDao) {
        this.quotaChangeRequestDao = quotaChangeRequestDao;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public BigInteger refresh(Long campaignId) {
        Map<String, BigInteger> owningCost = new HashMap<>();
        Consumer<BigInteger> consumer = (bigInteger -> owningCost.compute(KEY, (k, v) -> v == null
                ? bigInteger
                : v.add(bigInteger)));

        int i = 0;
        Long fromId = null;

        while ((fromId = execute(fromId, campaignId, consumer)) != null) {
            if (++i > MAX_REPEATS) {
                LOG.warn("Infinity loop detected!");
                return null;
            }
        }

        return owningCost.getOrDefault(KEY, BigInteger.ZERO);
    }

    private Long execute(Long fromId, Long campaignId, Consumer<BigInteger> consumer) {
        OptionalLong max = getRequests(fromId, campaignId).stream()
                .peek(requestOwningCost -> consumer.accept(BigInteger.valueOf(requestOwningCost.get2())))
                .mapToLong(Tuple2::get1)
                .max();

        return max.isPresent() ? max.getAsLong() : null;
    }

    private List<Tuple2<Long, Long>> getRequests(Long fromId, Long campaignId) {
        return quotaChangeRequestDao.readRequestOwningCostByCampaignId(fromId, campaignId, LIMIT, VALID_STATUSES);
    }
}
