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

import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.SetMultimap;
import ru.yandex.qe.dispenser.domain.bot.Configuration;
import ru.yandex.qe.dispenser.domain.bot.ConfigurationWithComponents;
import ru.yandex.qe.dispenser.domain.dao.SqlDaoBase;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class SqlPreOrderRequestDao extends SqlDaoBase implements BotPreOrderRequestDao {
    private static final String GET_REQUEST_IDS_BY_PREORDER_ID_QUERY = "SELECT pre_order_id, quota_request_id FROM pre_order_quota_request WHERE pre_order_id IN (:preOrderId)";
    private static final String REMOVE_ORDER_REQUESTS_QUERY = "DELETE FROM pre_order_quota_request WHERE pre_order_id IN (:preOrderId)";
    private static final String INSERT_ORDER_REQUESTS_QUERY = "INSERT INTO pre_order_quota_request (pre_order_id, quota_request_id, server_quantity, cost) VALUES (:preOrderId, :quotaRequestId, :serverQuantity, :cost)";

    private static final String GET_PREORDER_IDS_BY_REQUEST_ID_QUERY = "SELECT pre_order_id, quota_request_id FROM pre_order_quota_request WHERE quota_request_id IN (:quotaRequestId)";

    private static final String GET_EXTENDED_PREORDERS = "SELECT poqr.*, bpo.server_quantity as pre_order_server_quantity, bpo.name as pre_order_name, bpo.service_id as pre_order_service_id, bpo.reserve_rate as pre_order_reserve_rate, bc.json, bpo.bot_big_order_config_id FROM pre_order_quota_request poqr JOIN bot_pre_order bpo ON bpo.id = poqr.pre_order_id JOIN bot_configuration bc ON bc.id = bpo.server_id WHERE poqr.quota_request_id IN (:requestId)";

    private static final String GET_PREORDER_BY_BIG_ORDER_ID = "SELECT poqr.* " +
            "FROM pre_order_quota_request poqr " +
            "JOIN bot_pre_order bpo ON bpo.id = poqr.pre_order_id " +
            "WHERE bpo.bot_big_order_id IN (:bigOrderId)";

    private static final String CLEAR_QUERY = "TRUNCATE pre_order_quota_request";

    @Override
    public Set<Long> getRequestsIdsByPreorderId(final long id) {
        return jdbcTemplate.queryForSet(GET_REQUEST_IDS_BY_PREORDER_ID_QUERY,
                Collections.singletonMap("preOrderId", id), (rs, ignored) -> rs.getLong("quota_request_id"));
    }

    @Override
    public void removeOrderRequests(final Set<Long> preOrderIds) {
        if (preOrderIds.isEmpty()) {
            return;
        }
        jdbcTemplate.update(REMOVE_ORDER_REQUESTS_QUERY, Collections.singletonMap("preOrderId", preOrderIds));
    }

    @Override
    public void createOrderRequests(final Collection<PreOrderRequest> requests) {
        if (requests.isEmpty()) {
            return;
        }

        final List<Map<String, ?>> params = requests.stream()
                .map(request -> ImmutableMap.of(
                        "quotaRequestId", request.getKey().getRequestId(),
                        "preOrderId", request.getKey().getPreOrderId(),
                        "serverQuantity", request.getValue().getServerQuantity(),
                        "cost", request.getValue().getCost()
                ))
                .collect(Collectors.toList());

        jdbcTemplate.batchUpdate(INSERT_ORDER_REQUESTS_QUERY, params);
    }

    @Override
    public SetMultimap<Long, Long> getRequestsIdsByPreOrderIds(final Set<Long> preOrdersIds) {
        final HashMultimap<Long, Long> result = HashMultimap.create();
        if (preOrdersIds.isEmpty()) {
            return result;
        }
        jdbcTemplate.query(GET_REQUEST_IDS_BY_PREORDER_ID_QUERY,
                Collections.singletonMap("preOrderId", preOrdersIds), (rs) ->
                        result.put(rs.getLong("pre_order_id"), rs.getLong("quota_request_id"))
        );
        return result;
    }

    @Override
    public SetMultimap<Long, Long> getPreOrderIdsByRequestId(final Collection<Long> requestsIds) {
        final HashMultimap<Long, Long> result = HashMultimap.create();
        if (requestsIds.isEmpty()) {
            return result;
        }
        jdbcTemplate.query(GET_PREORDER_IDS_BY_REQUEST_ID_QUERY,
                Collections.singletonMap("quotaRequestId", requestsIds), (rs) ->
                        result.put(rs.getLong("quota_request_id"), rs.getLong("pre_order_id"))
        );
        return result;
    }

    @Override
    public Collection<ExtendedPreOrderRequest> getRequestPreorders(final Set<Long> requestIds) {
        if (requestIds.isEmpty()) {
            return Collections.emptyList();
        }
        return jdbcTemplate.query(GET_EXTENDED_PREORDERS, Collections.singletonMap("requestId", requestIds), SqlPreOrderRequestDao::toExtendedPreOrderRequest);
    }

    @Override
    public Collection<PreOrderRequest> getRequestPreordersByBigOrderIds(final Set<Long> bigOrderIds) {
        if (bigOrderIds.isEmpty()) {
            return Collections.emptyList();
        }

        return jdbcTemplate.query(GET_PREORDER_BY_BIG_ORDER_ID, Collections.singletonMap("bigOrderId", bigOrderIds), this::toPreOrderRequest);
    }

    private PreOrderRequest toPreOrderRequest(final ResultSet rs, final int ignored) throws SQLException {
        return new PreOrderRequest(
                new PreOrderRequest.Key(rs.getLong("pre_order_id"), rs.getLong("quota_request_id")),
                new PreOrderRequest.Value(rs.getDouble("server_quantity"), rs.getDouble("cost"))
        );
    }

    private static ExtendedPreOrderRequest toExtendedPreOrderRequest(final ResultSet rs, final int ignored) throws SQLException {
        final Configuration configuration = getJson(rs, "json", ConfigurationWithComponents.class);

        return new ExtendedPreOrderRequest(
                rs.getLong("quota_request_id"),
                rs.getLong("pre_order_id"),
                rs.getDouble("server_quantity"),
                rs.getDouble("cost"),
                configuration.getFullName(),
                rs.getLong("pre_order_server_quantity"),
                rs.getString("pre_order_name"),
                rs.getLong("pre_order_service_id"),
                rs.getDouble("pre_order_reserve_rate"),
                rs.getLong("bot_big_order_config_id")
        );
    }

    @Override
    public boolean clear() {
        return jdbcTemplate.update(CLEAR_QUERY) > 0;
    }
}
