package de.avatic.lcc.repositories.calculation;

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

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

@Repository
public class CalculationJobDestinationRepository {

    private final JdbcTemplate jdbcTemplate;

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

    @Transactional
    public List<CalculationJobDestination> getDestinationsByJobId(Integer jobId) {

        String query = "SELECT * FROM calculation_job_destination WHERE calculation_job_id = ?";

        return jdbcTemplate.query(query, new CalculationJobDestinationMapper(), jobId);

    }

    @Transactional
    public Integer insert(CalculationJobDestination destination) {
        String sql = """
            INSERT INTO calculation_job_destination (
                calculation_job_id, premise_destination_id, shipping_frequency, total_cost, annual_amount,
                annual_risk_cost, annual_chance_cost, is_small_unit, annual_repacking_cost, annual_handling_cost,
                annual_disposal_cost, operational_stock, safety_stock, stocked_inventory, in_transport_stock,
                stock_before_payment, annual_capital_cost, annual_storage_cost, custom_value, custom_duties,
                tariff_rate, annual_custom_cost, air_freight_share_max, air_freight_share, air_freight_volumetric_weight,
                air_freight_weight, annual_air_freight_cost, container_type, hu_count, layer_structure,
                layer_count, transport_weight_exceeded, annual_transportation_cost, container_utilization,
                transit_time_in_days, safety_stock_in_days, material_cost, fca_cost, is_d2d, rate_d2d
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """;

        KeyHolder keyHolder = new GeneratedKeyHolder();

        jdbcTemplate.update(connection -> {
            PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            int paramIndex = 1;

            // Core identifiers
            ps.setObject(paramIndex++, destination.getCalculationJobId());
            ps.setObject(paramIndex++, destination.getPremiseDestinationId());
            ps.setObject(paramIndex++, destination.getShippingFrequency());
            ps.setObject(paramIndex++, destination.getTotalCost());
            ps.setObject(paramIndex++, destination.getAnnualAmount());

            // Risk calculations
            ps.setObject(paramIndex++, destination.getTotalRiskCost());
            ps.setObject(paramIndex++, destination.getTotalChanceCost());

            // Handling costs
            ps.setObject(paramIndex++, destination.getSmallUnit());
            ps.setObject(paramIndex++, destination.getAnnualRepackingCost());
            ps.setObject(paramIndex++, destination.getAnnualHandlingCost());
            ps.setObject(paramIndex++, destination.getAnnualDisposalCost());

            // Inventory management
            ps.setObject(paramIndex++, destination.getOperationalStock());
            ps.setObject(paramIndex++, destination.getSafetyStock());
            ps.setObject(paramIndex++, destination.getStockedInventory());
            ps.setObject(paramIndex++, destination.getInTransportStock());
            ps.setObject(paramIndex++, destination.getStockBeforePayment());
            ps.setObject(paramIndex++, destination.getAnnualCapitalCost());
            ps.setObject(paramIndex++, destination.getAnnualStorageCost());

            // Customs
            ps.setObject(paramIndex++, destination.getCustomValue());
            ps.setObject(paramIndex++, destination.getCustomDuties());
            ps.setObject(paramIndex++, destination.getTariffRate());
            ps.setObject(paramIndex++, destination.getAnnualCustomCost());

            // Air freight risk
            ps.setObject(paramIndex++, destination.getAirFreightShareMax());
            ps.setObject(paramIndex++, destination.getAirFreightShare());
            ps.setObject(paramIndex++, destination.getAirFreightVolumetricWeight());
            ps.setObject(paramIndex++, destination.getAirFreightWeight());
            ps.setObject(paramIndex++, destination.getAnnualAirFreightCost());

            // Transportation
            ps.setObject(paramIndex++, destination.getContainerType().name());
            ps.setObject(paramIndex++, destination.getHuCount());
            ps.setObject(paramIndex++, destination.getLayerStructure());
            ps.setObject(paramIndex++, destination.getLayerCount());
            ps.setObject(paramIndex++, destination.getTransportWeightExceeded());
            ps.setObject(paramIndex++, destination.getAnnualTransportationCost());
            ps.setObject(paramIndex++, destination.getContainerUtilization());
            ps.setObject(paramIndex++, destination.getTotalTransitTime());
            ps.setObject(paramIndex++, destination.getSafetyStockInDays());

            // Material costs
            ps.setObject(paramIndex++, destination.getMaterialCost());
            ps.setObject(paramIndex++, destination.getFcaCost());

            ps.setObject(paramIndex++, destination.getD2D());
            ps.setObject(paramIndex++, destination.getRateD2D());

            return ps;
        }, keyHolder);

        Number generatedKey = keyHolder.getKey();
        if (generatedKey != null) {
            return generatedKey.intValue();
        } else {
            throw new DatabaseException("Failed to retrieve generated key for inserted CalculationJobDestination");
        }
    }




    private static class CalculationJobDestinationMapper implements RowMapper<CalculationJobDestination> {
        @Override
        public CalculationJobDestination mapRow(ResultSet rs, int rowNum) throws SQLException {

            CalculationJobDestination entity = new CalculationJobDestination();

            // Basic fields already mapped
            entity.setId(rs.getInt("id"));
            entity.setPremiseDestinationId(rs.getInt("premise_destination_id"));
            entity.setCalculationJobId(rs.getInt("calculation_job_id"));
            entity.setSafetyStock(rs.getBigDecimal("safety_stock"));
            entity.setSafetyStockInDays(rs.getBigDecimal("safety_stock_in_days"));
            entity.setShippingFrequency(rs.getInt("shipping_frequency"));
            entity.setTotalCost(rs.getBigDecimal("total_cost"));
            entity.setAnnualAmount(rs.getBigDecimal("annual_amount"));

            // Risk fields
            entity.setTotalRiskCost(rs.getBigDecimal("annual_risk_cost"));
            entity.setTotalChanceCost(rs.getBigDecimal("annual_chance_cost"));

            // Handling fields
            entity.setSmallUnit(rs.getBoolean("is_small_unit"));
            entity.setAnnualRepackingCost(rs.getBigDecimal("annual_repacking_cost"));
            entity.setAnnualHandlingCost(rs.getBigDecimal("annual_handling_cost"));
            entity.setAnnualDisposalCost(rs.getBigDecimal("annual_disposal_cost"));

            // Inventory fields
            entity.setOperationalStock(rs.getBigDecimal("operational_stock"));
            // Safety stock already set above
            entity.setStockedInventory(rs.getBigDecimal("stocked_inventory"));
            entity.setInTransportStock(rs.getBigDecimal("in_transport_stock"));
            entity.setStockBeforePayment(rs.getBigDecimal("stock_before_payment"));
            entity.setAnnualCapitalCost(rs.getBigDecimal("annual_capital_cost"));
            entity.setAnnualStorageCost(rs.getBigDecimal("annual_storage_cost"));

            // Custom fields
            entity.setCustomValue(rs.getBigDecimal("custom_value"));
            entity.setCustomDuties(rs.getBigDecimal("custom_duties"));
            entity.setTariffRate(rs.getBigDecimal("tariff_rate"));
            entity.setAnnualCustomCost(rs.getBigDecimal("annual_custom_cost"));

            // Air Freight Risk fields
            entity.setAirFreightShareMax(rs.getBigDecimal("air_freight_share_max"));
            entity.setAirFreightShare(rs.getBigDecimal("air_freight_share"));
            entity.setAirFreightVolumetricWeight(rs.getBigDecimal("air_freight_volumetric_weight"));
            entity.setAirFreightWeight(rs.getBigDecimal("air_freight_weight"));
            entity.setAnnualAirFreightCost(rs.getBigDecimal("annual_air_freight_cost"));

            // Transportation fields
            entity.setContainerType(ContainerType.valueOf(rs.getString("container_type")));
            entity.setHuCount(rs.getInt("hu_count"));
            entity.setLayerStructure(rs.getString("layer_structure"));
            entity.setLayerCount(rs.getInt("layer_count"));
            entity.setTransportWeightExceeded(rs.getBoolean("transport_weight_exceeded"));
            entity.setShippingFrequency(rs.getInt("shipping_frequency"));
            entity.setAnnualTransportationCost(rs.getBigDecimal("annual_transportation_cost"));
            entity.setTotalTransitTime(rs.getInt("transit_time_in_days"));
            entity.setContainerUtilization(rs.getBigDecimal("container_utilization"));

            // Material Cost fields
            entity.setMaterialCost(rs.getBigDecimal("material_cost"));
            entity.setFcaCost(rs.getBigDecimal("fca_cost"));

            return entity;

        }
    }
}
