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

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

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.ImmutableMap;
import org.postgresql.util.PGobject;

import ru.yandex.qe.dispenser.domain.bot.PreOrder;
import ru.yandex.qe.dispenser.domain.dao.SqlUtils;
import ru.yandex.qe.dispenser.domain.dao.bot.BotPreOrder;
import ru.yandex.qe.dispenser.domain.dao.bot.SqlSyncableBotDao;

public class SqlBotPreOrderDao extends SqlSyncableBotDao<BotPreOrder> implements BotPreOrderDao {

    public static final TypeReference<List<PreOrder.Components>> COMPONENTS_TYPE = new TypeReference<List<PreOrder.Components>>() {
    };
    private static final String GET_ID_BY_BIG_ORDER_CONFIG_ID_QUERY = "SELECT id FROM bot_synced_pre_order " +
            "WHERE big_order_config_id in (:big_order_config_id)";
    private static final String GET_NOT_MAPPED_PRE_ORDERS_BY_BIG_ORDER_ID_QUERY = "SELECT * FROM bot_synced_pre_order " +
            "WHERE big_order_id in (:big_order_id) " +
            "AND deleted = false " +
            "AND id NOT IN (SELECT id FROM bot_pre_order WHERE bot_big_order_id in (:big_order_id))";

    private static final String GET_ALL_LINKED_CONFIGURATION_IDS = "SELECT DISTINCT server_id as id FROM bot_synced_pre_order " +
            "WHERE server_id IS NOT NULL UNION SELECT DISTINCT storage_id as id FROM bot_synced_pre_order WHERE storage_id IS NOT NULL";

    public SqlBotPreOrderDao() {
        super("bot_synced_pre_order", ImmutableMap.<String, String>builder()
                .put("big_order_id", ":big_order_id")
                .put("big_order_config_id", ":big_order_config_id")
                .put("category_code", ":category_code")
                .put("status_id", ":status_id")
                .put("server_id", ":server_id")
                .put("server_quantity", ":server_quantity")
                .put("storage_id", ":storage_id")
                .put("storage_quantity", ":storage_quantity")
                .put("oebs_service_id", ":oebs_service_id")
                .put("ticket_id", ":ticket_id")
                .put("customer_comment", ":customer_comment")
                .put("responsible", ":responsible")
                .put("components", ":components")
                .put("deleted", ":deleted")
                .put("abc_service_id", ":abc_service_id")
                .put("upgrades_cost", ":upgrades_cost")
                .build());
    }

    @Override
    protected BotPreOrder toItem(final ResultSet rs, final int rowNum) throws SQLException {
        final PGobject components = (PGobject) rs.getObject("components");
        return new BotPreOrder.Builder()
                .id(rs.getLong("id"))
                .bigOrderId(rs.getLong("big_order_id"))
                .bigOrderConfigId(rs.getLong("big_order_config_id"))
                .categoryCode(rs.getString("category_code"))
                .statusId(rs.getInt("status_id"))
                .server(getLong(rs, "server_id"))
                .serverQuantity(getLong(rs, "server_quantity"))
                .storage(getLong(rs, "storage_id"))
                .storageQuantity(getLong(rs, "storage_quantity"))
                .oebsServiceId(rs.getLong("oebs_service_id"))
                .ticketId(rs.getString("ticket_id"))
                .customerComment(rs.getString("customer_comment"))
                .responsible(rs.getString("responsible"))
                .components(components == null ? null : SqlUtils.fromJsonb(components, COMPONENTS_TYPE))
                .deleted(rs.getBoolean("deleted"))
                .abcServiceId(rs.getInt("abc_service_id"))
                .upgradesCost(rs.getLong("upgrades_cost"))
                .build();
    }

    @Override
    protected Map<String, Object> toParams(final BotPreOrder botPreOrder) {
        final HashMap<String, Object> params = new HashMap<>();
        params.put("id", botPreOrder.getId());
        params.put("big_order_id", botPreOrder.getBigOrderId());
        params.put("big_order_config_id", botPreOrder.getBigOrderConfigId());
        params.put("category_code", botPreOrder.getCategoryCode());
        params.put("status_id", botPreOrder.getStatusId());
        params.put("server_id", botPreOrder.getServerId());
        params.put("server_quantity", botPreOrder.getServerQuantity());
        params.put("storage_id", botPreOrder.getStorageId());
        params.put("storage_quantity", botPreOrder.getStorageQuantity());
        params.put("oebs_service_id", botPreOrder.getOebsServiceId());
        params.put("ticket_id", botPreOrder.getTicketId());
        params.put("customer_comment", botPreOrder.getCustomerComment());
        params.put("responsible", botPreOrder.getResponsible());
        params.put("components", botPreOrder.getComponents() == null ? null : SqlUtils.toJsonb(botPreOrder.getComponents()));
        params.put("deleted", botPreOrder.isDeleted());
        params.put("abc_service_id", botPreOrder.getAbcServiceId());
        params.put("upgrades_cost", botPreOrder.getUpgradesCost());
        return params;
    }

    @Override
    public Set<Long> getPreOrderIdsByBigOrderConfigIds(final Set<Long> bigOrderConfigIds) {
        if (bigOrderConfigIds.isEmpty()) {
            return Collections.emptySet();
        }
        return jdbcTemplate.queryForSet(GET_ID_BY_BIG_ORDER_CONFIG_ID_QUERY, Collections.singletonMap("big_order_config_id", bigOrderConfigIds), (r, ignorede) -> r.getLong("id"));
    }

    @Override
    public Collection<BotPreOrder> getNotMappedPreOrderIdsByBigOrderIds(final Set<Long> bigOrderIds) {
        if (bigOrderIds.isEmpty()) {
            return Collections.emptyList();
        }
        return jdbcTemplate.queryForSet(GET_NOT_MAPPED_PRE_ORDERS_BY_BIG_ORDER_ID_QUERY,
                Collections.singletonMap("big_order_id", bigOrderIds), this::toItem);
    }

    @Override
    public Set<Long> getAllLinkedConfigurationIds() {
        return jdbcTemplate.queryForSet(GET_ALL_LINKED_CONFIGURATION_IDS, (rs, i) -> rs.getLong("id"));
    }
}
