package de.avatic.lcc.repositories.premise;

import de.avatic.lcc.model.db.premises.route.Route;
import de.avatic.lcc.util.exception.internalerror.DatabaseException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

@Repository
public class RouteRepository {

    private final JdbcTemplate jdbcTemplate;

    public RouteRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public List<Route> getByDestinationId(Integer id) {
        String query = "SELECT * FROM premise_route WHERE premise_destination_id = ?";
        return jdbcTemplate.query(query, new RouteMapper(), id);
    }

    public Optional<Route> getSelectedByDestinationId(Integer id) {
        String query = "SELECT * FROM premise_route WHERE premise_destination_id = ? AND is_selected = TRUE";
        var route = jdbcTemplate.query(query, new RouteMapper(), id);

        if(route.isEmpty()) {
            return Optional.empty();
        }

        if(1 < route.size())
            throw new DatabaseException("Multiple selected routes for destination with id " + id);


        return Optional.of(route.getFirst());
    }

    public void deleteAllById(List<Integer> ids) {
        if (ids == null || ids.isEmpty()) {
            return;
        }

        // Create a parameterized SQL statement with placeholders for each ID
        String placeholders = String.join(",", Collections.nCopies(ids.size(), "?"));
        String sql = "DELETE FROM premise_route WHERE id IN (" + placeholders + ")";

        jdbcTemplate.update(sql, ids.toArray(new Object[0]));
    }

    public Integer insert(Route premiseRoute) {
        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update(connection -> {
            PreparedStatement ps = connection.prepareStatement(
                    "INSERT INTO premise_route (is_cheapest, is_fastest, is_selected, premise_destination_id) " +
                            "VALUES (?, ?, ?, ?)",
                    Statement.RETURN_GENERATED_KEYS);

            ps.setBoolean(1, premiseRoute.getCheapest());
            ps.setBoolean(2, premiseRoute.getFastest());
            ps.setBoolean(3, premiseRoute.getSelected());
            ps.setInt(4, premiseRoute.getDestinationId());

            return ps;
        }, keyHolder);

        return keyHolder.getKey() != null ? keyHolder.getKey().intValue() : null;
    }

    public void updateSelectedByDestinationId(Integer destinationId, Integer selectedRouteId) {
        String deselectQuery = """
                UPDATE premise_route SET is_selected = FALSE WHERE is_selected = TRUE AND premise_destination_id = ?
                """;
        String selectQuery = """
                UPDATE premise_route SET is_selected = TRUE WHERE id = ?
                """;

        jdbcTemplate.update(deselectQuery, destinationId);
        var affectedRowsSelect = jdbcTemplate.update(selectQuery, selectedRouteId);

        if (1 != affectedRowsSelect) {
            throw new DatabaseException("Unable to select route with id " +  selectedRouteId);
        }

    }

    private static class RouteMapper implements RowMapper<Route> {
        @Override
        public Route mapRow(ResultSet rs, int rowNum) throws SQLException {
            Route entity = new Route();

            entity.setId(rs.getInt("id"));
            entity.setCheapest(rs.getBoolean("is_cheapest"));
            entity.setFastest(rs.getBoolean("is_fastest"));
            entity.setSelected(rs.getBoolean("is_selected"));
            entity.setDestinationId(rs.getInt("premise_destination_id"));

            return entity;
        }
    }
}
