package de.avatic.lcc.repositories.calculation;

import de.avatic.lcc.dto.generic.RateType;
import de.avatic.lcc.dto.generic.TransportType;
import de.avatic.lcc.model.db.calculations.CalculationJobRouteSection;
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 org.springframework.transaction.annotation.Transactional;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;

@Repository
public class CalculationJobRouteSectionRepository {

    private final JdbcTemplate jdbcTemplate;

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

    @Transactional
    public Integer insert(CalculationJobRouteSection section) {
        KeyHolder keyHolder = new GeneratedKeyHolder();

        String query = """
                INSERT INTO calculation_job_route_section (
                    premise_route_section_id,
                    calculation_job_destination_id,
                    transport_type,
                    is_unmixed_price,
                    is_cbm_price,
                    is_weight_price,
                    is_stacked,
                    is_pre_run,
                    is_main_run,
                    is_post_run,
                    rate,
                    distance,
                    cbm_price,
                    weight_price,
                    annual_cost,
                    transit_time
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """;

        jdbcTemplate.update(connection -> {
            var ps = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);

            if (section.getPremiseRouteSectionId() == null)
                ps.setNull(1, java.sql.Types.INTEGER);
            else
                ps.setInt(1, section.getPremiseRouteSectionId());

            ps.setInt(2, section.getCalculationJobDestinationId());
            ps.setString(3, convertRateTypeToString(section.getRateType(), section.getTransportType()));
            ps.setBoolean(4, section.getUnmixedPrice());
            ps.setBoolean(5, section.isCbmPrice());
            ps.setBoolean(6, section.isWeightPrice());
            ps.setBoolean(7, section.getStacked());
            ps.setBoolean(8, section.getPreRun());
            ps.setBoolean(9, section.getMainRun());
            ps.setBoolean(10, section.getPostRun());
            ps.setBigDecimal(11, section.getRate());
            ps.setBigDecimal(12, section.getDistance());
            ps.setBigDecimal(13, section.getCbmPrice());
            ps.setBigDecimal(14, section.getWeightPrice());
            ps.setBigDecimal(15, section.getAnnualCost());
            ps.setInt(16, section.getTransitTime());
            return ps;
        }, keyHolder);

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

    private String convertRateTypeToString(RateType rateType, TransportType transportType) {
        if (rateType == null) {
            return null;
        }

        return switch (rateType) {
            case NEAR_BY -> RateType.MATRIX.name();
            case MATRIX, D2D -> rateType.name();
            case CONTAINER -> transportType == null ? null : transportType.name();
        };
    }


    @Transactional
    public List<CalculationJobRouteSection> getRouteSectionsByDestinationId(Integer destinationId) {
        String query = "SELECT * FROM calculation_job_route_section WHERE calculation_job_destination_id = ?";
        return jdbcTemplate.query(query, new CalculationJobRouteSectionMapper(), destinationId);
    }

    @Transactional
    public Map<Integer, List<CalculationJobRouteSection>> getRouteSectionsByDestinationIds(List<Integer> destinationIds) {
        if (destinationIds == null || destinationIds.isEmpty()) {
            return Collections.emptyMap();
        }

        String placeholders = String.join(",", Collections.nCopies(destinationIds.size(), "?"));
        String query = "SELECT * FROM calculation_job_route_section WHERE calculation_job_destination_id IN (" + placeholders + ")";

        List<CalculationJobRouteSection> allSections = jdbcTemplate.query(
                query,
                new CalculationJobRouteSectionMapper(),
                destinationIds.toArray()
        );

        // Group by destination ID
        Map<Integer, List<CalculationJobRouteSection>> groupedSections = new HashMap<>();
        for (CalculationJobRouteSection section : allSections) {
            groupedSections
                    .computeIfAbsent(section.getCalculationJobDestinationId(), k -> new ArrayList<>())
                    .add(section);
        }

        return groupedSections;
    }

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

            // Basic fields
            entity.setId(rs.getInt("id"));
            entity.setPremiseRouteSectionId(rs.getInt("premise_route_section_id"));
            entity.setCalculationJobDestinationId(rs.getInt("calculation_job_destination_id"));

            // Rule and price type flags
            String transportType = rs.getString("transport_type");

            if ("MATRIX".equals(transportType)) {
                entity.setTransportType(TransportType.ROAD);
                entity.setRateType(RateType.MATRIX);
            } else if ("D2D".equals(transportType)) {
                entity.setTransportType(TransportType.ROAD);
                entity.setRateType(RateType.D2D);
            } else {
                entity.setRateType(RateType.CONTAINER);
                entity.setTransportType(TransportType.valueOf(rs.getString("transport_type")));
            }


            entity.setUnmixedPrice(rs.getBoolean("is_unmixed_price"));
            entity.setCbmPrice(rs.getBoolean("is_cbm_price"));
            entity.setWeightPrice(rs.getBoolean("is_weight_price"));

            // Route section properties
            entity.setStacked(rs.getBoolean("is_stacked"));
            entity.setPreRun(rs.getBoolean("is_pre_run"));
            entity.setMainRun(rs.getBoolean("is_main_run"));
            entity.setPostRun(rs.getBoolean("is_post_run"));

            // Pricing information
            entity.setRate(rs.getBigDecimal("rate"));
            entity.setDistance(rs.getBigDecimal("distance"));
            entity.setCbmPrice(rs.getBigDecimal("cbm_price"));
            entity.setWeightPrice(rs.getBigDecimal("weight_price"));
            entity.setAnnualCost(rs.getBigDecimal("annual_cost"));

            // Transit time
            entity.setTransitTime(rs.getInt("transit_time"));

            return entity;
        }
    }


}
