package ru.yandex.qe.dispenser.domain.dao.resource.unit;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.ParametersAreNonnullByDefault;

import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.NotNull;
import org.postgresql.util.PGobject;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ru.yandex.qe.dispenser.domain.ResourceUnit;
import ru.yandex.qe.dispenser.domain.dao.SqlDaoBase;
import ru.yandex.qe.dispenser.domain.dao.SqlUtils;

@ParametersAreNonnullByDefault
public class SqlResourceUnitDao extends SqlDaoBase implements ResourceUnitDao {

    private static final String SELECT_ALL_QUERY = "SELECT * FROM resource_unit_config";
    private static final String SELECT_BY_RESOURCE_ID_QUERY = "SELECT * FROM resource_unit_config WHERE resource_id IN (:resourceId)";

    private static final String INSERT_QUERY = "INSERT INTO resource_unit_config (resource_id, default_unit, settings) VALUES (:resourceId, :defaultUnit, :settings)";

    private static final String UPDATE_QUERY = "UPDATE resource_unit_config SET default_unit = :defaultUnit, settings = :settings WHERE resource_id = :resourceId";

    private static final String DELETE_QUERY = "DELETE FROM resource_unit_config WHERE resource_id = :resourceId";
    private static final String TRUNCATE_QUERY = "TRUNCATE resource_unit_config";


    @Override
    @NotNull
    public Set<ResourceUnit> getAll() {
        return jdbcTemplate.queryForSet(SELECT_ALL_QUERY, this::toResourceUnit);
    }

    @NotNull
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public ResourceUnit create(final ResourceUnit resourceUnit) {
        jdbcTemplate.update(INSERT_QUERY, toParams(resourceUnit));
        return resourceUnit;
    }

    @NotNull
    @Override
    public ResourceUnit read(final Long id) throws EmptyResultDataAccessException {
        return jdbcTemplate.queryForObject(SELECT_BY_RESOURCE_ID_QUERY, ImmutableMap.of("resourceId", id), this::toResourceUnit);
    }

    @NotNull
    @Override
    public Map<Long, ResourceUnit> read(@NotNull final Collection<Long> ids) {
        if (ids.isEmpty()) {
            return Collections.emptyMap();
        }
        final Set<ResourceUnit> units = jdbcTemplate.queryForSet(SELECT_BY_RESOURCE_ID_QUERY, ImmutableMap.of("resourceId", ids), this::toResourceUnit);

        return units.stream()
                .collect(Collectors.toMap(ResourceUnit::getResourceId, Function.identity()));
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean update(final ResourceUnit resourceUnit) {
        return jdbcTemplate.update(UPDATE_QUERY, toParams(resourceUnit)) > 0;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean delete(final ResourceUnit resourceUnit) {
        return jdbcTemplate.update(DELETE_QUERY, toParams(resourceUnit)) > 0;
    }

    private ResourceUnit toResourceUnit(final ResultSet rs, final int ignored) throws SQLException {
        return new ResourceUnit(rs.getLong("resource_id"), rs.getString("default_unit"), SqlUtils.fromJsonb((PGobject) rs.getObject("settings"), ResourceUnit.UnitsSettings.class));
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean clear() {
        return jdbcTemplate.update(TRUNCATE_QUERY) > 0;
    }

    private Map<String, ?> toParams(final ResourceUnit resourceUnit) {
        final HashMap<String, Object> params = new HashMap<>();

        params.put("resourceId", resourceUnit.getResourceId());
        params.put("defaultUnit", resourceUnit.getDefaultUnit());
        params.put("settings", SqlUtils.toJsonb(resourceUnit.getUnitsSettings()));

        return params;
    }
}
