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

import org.jetbrains.annotations.NotNull;
import org.springframework.transaction.annotation.Transactional;
import ru.yandex.qe.dispenser.domain.Segment;
import ru.yandex.qe.dispenser.domain.Service;
import ru.yandex.qe.dispenser.domain.bot.BotServiceConfiguration;
import ru.yandex.qe.dispenser.domain.bot.ConfigurationWithComponents;
import ru.yandex.qe.dispenser.domain.dao.SqlDaoBase;
import ru.yandex.qe.dispenser.domain.hierarchy.Hierarchy;
import ru.yandex.qe.dispenser.domain.util.MapBuilder;

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;

import static ru.yandex.qe.dispenser.domain.util.CollectionUtils.ids;

public class SqlBotServiceConfigurationDao extends SqlDaoBase implements BotServiceConfigurationDao {

    public static final String GET_ALL_ID_QUERY = "SELECT bot_configuration_id FROM bot_service_configuration";
    public static final String GET_BY_SERVICE_QUERY = "SELECT * FROM bot_service_configuration WHERE service_id in (:serviceId)";
    public static final String GET_BY_SERVICE_AND_CAMPAIGN_GROUP_QUERY = "SELECT * FROM bot_service_configuration WHERE service_id in (:serviceId) AND bot_campaign_group_id = :botCampaignGroupId";
    public static final String GET_BY_SERVICE_WITH_CONFIGURATION_QUERY = "SELECT * FROM bot_service_configuration JOIN bot_configuration ON bot_configuration.id = bot_service_configuration.bot_configuration_id WHERE service_id = :serviceId";
    public static final String GET_BY_SERVICE_AND_CAMPAIGN_GROUP_WITH_CONFIGURATION_QUERY = "SELECT * FROM bot_service_configuration JOIN bot_configuration ON bot_configuration.id = bot_service_configuration.bot_configuration_id WHERE service_id = :serviceId AND bot_campaign_group_id = :botCampaignGroupId";

    public static final String INSERT_QUERY = "INSERT INTO bot_service_configuration (key, service_id, bot_configuration_id, location_segment_id, bot_campaign_group_id, valid) VALUES (:key, :serviceId, :botConfigurationId, :locationSegmentId, :botCampaignGroupId, :valid)";
    public static final String CLEAR_SERVICE_QUERY_AND_CAMPAIGN_GROUP = "DELETE FROM bot_service_configuration WHERE service_id = :serviceId AND bot_campaign_group_id = :botCampaignGroupId";

    public static final String CLEAR_QUERY = "TRUNCATE bot_service_configuration CASCADE";

    public static final String UPDATE_QUERY = "UPDATE bot_service_configuration SET valid = :valid WHERE service_id = :serviceId AND bot_configuration_id = :botConfigurationId AND location_segment_id = :locationSegmentId AND key = :key AND bot_campaign_group_id = :botCampaignGroupId";


    @Override
    public Set<BotServiceConfiguration> getByService(final Service service) {
        return getByServices(Collections.singleton(service));
    }

    @Override
    public Set<BotServiceConfiguration> getByService(final Service service, final long botCampaignGroupId) {
        return getByServices(Collections.singleton(service), botCampaignGroupId);
    }

    @Override
    public Set<BotServiceConfiguration> getByServices(final Collection<Service> services) {
        if (services.isEmpty()) {
            return Collections.emptySet();
        }
        return jdbcTemplate.queryForSet(GET_BY_SERVICE_QUERY, Map.of("serviceId", ids(services)),
                this::toConfiguration);
    }

    @Override
    public Set<BotServiceConfiguration> getByServices(final Collection<Service> services, final long botCampaignGroupId) {
        if (services.isEmpty()) {
            return Collections.emptySet();
        }
        return jdbcTemplate.queryForSet(GET_BY_SERVICE_AND_CAMPAIGN_GROUP_QUERY, Map.of("serviceId", ids(services),
                "botCampaignGroupId", botCampaignGroupId), this::toConfiguration);
    }

    @Override
    public Set<BotServiceConfiguration.View> getByServiceViews(final Service service, final long botCampaignGroupId) {
        return jdbcTemplate.queryForSet(GET_BY_SERVICE_AND_CAMPAIGN_GROUP_WITH_CONFIGURATION_QUERY, Map.of("serviceId", service.getId(),
                "botCampaignGroupId", botCampaignGroupId), this::toConfigurationView);
    }

    @Override
    public Set<BotServiceConfiguration.View> getByServiceViews(Service service) {
        return jdbcTemplate.queryForSet(GET_BY_SERVICE_WITH_CONFIGURATION_QUERY, Map.of("serviceId", service.getId()), this::toConfigurationView);
    }

    @Override
    @Transactional
    public void deleteByService(final Service service, final long botCampaignGroupId) {
        jdbcTemplate.update(CLEAR_SERVICE_QUERY_AND_CAMPAIGN_GROUP, Map.of("serviceId", service.getId(),
                "botCampaignGroupId", botCampaignGroupId));
    }

    @Override
    @Transactional
    public void createAll(final Set<BotServiceConfiguration> configurations) {
        if (configurations.isEmpty()) {
            return;
        }

        jdbcTemplate.batchUpdate(INSERT_QUERY, toParams(configurations));
    }

    @NotNull
    public List<Map<String, ?>> toParams(final Set<BotServiceConfiguration> configurations) {
        return configurations.stream()
                .map(configuration -> {
                    final Segment locationSegment = configuration.getLocationSegment();
                    return MapBuilder.<String, Object>of("serviceId", configuration.getService().getId())
                            .put("botConfigurationId", configuration.getConfigurationId())
                            .put("valid", configuration.isValid())
                            .put("locationSegmentId", locationSegment == null ? null : locationSegment.getId())
                            .put("key", configuration.getKey())
                            .put("botCampaignGroupId", configuration.getBotCampaignGroupId())
                            .build();
                })
                .collect(Collectors.toList());
    }

    @Override
    public Set<Long> getConfigurationsIds() {
        return jdbcTemplate.queryForSet(GET_ALL_ID_QUERY, (rs, num) -> rs.getLong("bot_configuration_id"));
    }

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

    @Override
    @Transactional
    public void updateAll(final Set<BotServiceConfiguration> configurations) {
        jdbcTemplate.batchUpdate(UPDATE_QUERY, toParams(configurations));
    }

    private BotServiceConfiguration.View toConfigurationView(final ResultSet rs, final int rowNum) throws SQLException {
        final ConfigurationWithComponents configuration = getJson(rs, "json", ConfigurationWithComponents.class);
        final Service service = Hierarchy.get().getServiceReader().read(rs.getLong("service_id"));

        final Long locationSegmentId = getLong(rs, "location_segment_id");

        final Segment locationSegment = locationSegmentId == null ? null : Hierarchy.get().getSegmentReader().read(locationSegmentId);
        final String key = rs.getString("key");
        final long botCampaignGroupId = rs.getLong("bot_campaign_group_id");

        return new BotServiceConfiguration.View(key, service.toView(), configuration,
                locationSegment == null ? null : locationSegment.toView(), botCampaignGroupId);
    }


    private BotServiceConfiguration toConfiguration(final ResultSet rs, final int rowNum) throws SQLException {
        final Service service = Hierarchy.get().getServiceReader().read(rs.getLong("service_id"));
        final Long locationSegmentId = getLong(rs, "location_segment_id");
        final Segment locationSegment = locationSegmentId == null ? null : Hierarchy.get().getSegmentReader().read(locationSegmentId);
        final String key = rs.getString("key");
        final long botCampaignGroupId = rs.getLong("bot_campaign_group_id");

        return new BotServiceConfiguration(key, service, rs.getLong("bot_configuration_id"), locationSegment, botCampaignGroupId, rs.getBoolean("valid"));
    }
}
