package ru.yandex.qe.dispenser.domain.dao.bot.preorder.request;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.SetMultimap;
import org.springframework.beans.factory.annotation.Autowired;
import ru.yandex.qe.dispenser.domain.dao.bot.CompleteBotConfiguration;
import ru.yandex.qe.dispenser.domain.dao.bot.MappedPreOrder;
import ru.yandex.qe.dispenser.domain.dao.bot.configuration.BotConfigurationDaoImpl;
import ru.yandex.qe.dispenser.domain.dao.bot.preorder.MappedPreOrderDaoImpl;
import ru.yandex.qe.dispenser.domain.util.MoreCollectors;
import ru.yandex.qe.dispenser.domain.util.MoreFunctions;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class PreOrderRequestDaoImpl implements BotPreOrderRequestDao {

    private final ConcurrentHashMap<PreOrderRequest.Key, PreOrderRequest> requestByKey = new ConcurrentHashMap<>();
    private final Multimap<Long, PreOrderRequest> requestsByPreOrderId = ArrayListMultimap.create();

    @Autowired
    private MappedPreOrderDaoImpl preOrderDao;
    @Autowired
    private BotConfigurationDaoImpl configurationDao;

    @Override
    public void removeOrderRequests(final Set<Long> preOrderIds) {
        preOrderIds.forEach(requestsByPreOrderId::removeAll);
        for (final PreOrderRequest.Key key : new HashSet<>(requestByKey.keySet())) {
            if (preOrderIds.contains(key.getPreOrderId())) {
                requestByKey.remove(key);
            }
        }
    }

    @Override
    public void createOrderRequests(final Collection<PreOrderRequest> requests) {
        for (final PreOrderRequest request : requests) {
            final PreOrderRequest.Key key = request.getKey();
            if (requestByKey.containsKey(key)) {
                throw new IllegalArgumentException("PreOrder request for " + key + " already exists");
            }
            if (!preOrderDao.readById(key.getPreOrderId()).isPresent()) {
                throw new IllegalArgumentException("PreOrder with id " + key.getPreOrderId() + " not exists");
            }
            requestByKey.put(key, request);
            requestsByPreOrderId.put(key.getPreOrderId(), request);
        }
    }

    @Override
    public Set<Long> getRequestsIdsByPreorderId(final long id) {
        return requestsByPreOrderId.get(id).stream()
                .map(pr -> pr.getKey().getRequestId())
                .collect(Collectors.toSet());
    }

    @Override
    public SetMultimap<Long, Long> getRequestsIdsByPreOrderIds(final Set<Long> preorderIds) {
        return requestsByPreOrderId.entries().stream()
                .filter(e -> preorderIds.contains(e.getKey()))
                .collect(MoreCollectors.toLinkedMultimap(Map.Entry::getKey, e -> e.getValue().getKey().getRequestId()));
    }

    @Override
    public SetMultimap<Long, Long> getPreOrderIdsByRequestId(final Collection<Long> requestIds) {
        return requestsByPreOrderId.entries().stream()
                .filter(e -> requestIds.contains(e.getValue().getKey().getRequestId()))
                .collect(MoreCollectors.toLinkedMultimap(e -> e.getValue().getKey().getRequestId(), Map.Entry::getKey));
    }

    @Override
    public Collection<ExtendedPreOrderRequest> getRequestPreorders(final Set<Long> requestIds) {
        final ArrayList<ExtendedPreOrderRequest> result = new ArrayList<>();
        for (final PreOrderRequest request : requestByKey.values()) {
            if (requestIds.contains(request.getKey().getRequestId())) {
                final MappedPreOrder preOrder = preOrderDao.readById(request.getKey().getPreOrderId()).get();
                final CompleteBotConfiguration configuration = (CompleteBotConfiguration) configurationDao.read(preOrder.getServerId());
                result.add(new ExtendedPreOrderRequest(request.getKey().getRequestId(),
                        request.getKey().getPreOrderId(),
                        request.getValue().getServerQuantity(),
                        request.getValue().getCost(),
                        configuration.getConfiguration().getFullName(),
                        preOrder.getServerQuantity(),
                        preOrder.getName(),
                        preOrder.getService().getId(),
                        preOrder.getReserveRate(),
                        preOrder.getBigOrderConfigId())
                );
            }
        }

        return result;
    }

    @Override
    public Collection<PreOrderRequest> getRequestPreordersByBigOrderIds(final Set<Long> bigOrderIds) {
        final Set<Long> preOrderIds = preOrderDao.getPreOrdersByBigOrderIds(bigOrderIds).keySet();
        return requestsByPreOrderId.entries().stream()
                .filter(e -> preOrderIds.contains(e.getKey()))
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());
    }

    public Map<Long, Double> getRequestTotalSumById(final Set<Long> requestIds) {
        return requestByKey.values().stream()
                .filter(r -> requestIds.contains(r.getKey().getRequestId()))
                .reduce(new HashMap<>(), (map, r) -> {
                    final long requestId = r.getKey().getRequestId();
                    map.put(requestId, map.getOrDefault(requestId, 0.0) + r.getValue().getCost());
                    return map;
                }, MoreFunctions.leftProjection());
    }

    @Override
    public boolean clear() {
        final boolean isEmpty = requestsByPreOrderId.isEmpty();
        requestsByPreOrderId.clear();
        requestByKey.clear();
        return !isEmpty;
    }
}
