package de.avatic.lcc.service.calculation.execution.steps;

import de.avatic.lcc.model.calculation.AirfreightResult;
import de.avatic.lcc.model.db.packaging.PackagingDimension;
import de.avatic.lcc.model.db.premises.Premise;
import de.avatic.lcc.model.db.premises.route.Destination;
import de.avatic.lcc.model.db.properties.CountryPropertyMappingId;
import de.avatic.lcc.model.db.properties.SystemPropertyMappingId;
import de.avatic.lcc.model.db.utils.DimensionUnit;
import de.avatic.lcc.model.db.utils.WeightUnit;
import de.avatic.lcc.repositories.country.CountryPropertyRepository;
import de.avatic.lcc.repositories.properties.PropertyRepository;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;

@Service
public class AirfreightCalculationService {

    private final PropertyRepository propertyRepository;
    private final PremiseToHuService premiseToHuService;
    private final CountryPropertyRepository countryPropertyRepository;

    public AirfreightCalculationService(PropertyRepository propertyRepository, PremiseToHuService premiseToHuService, CountryPropertyRepository countryPropertyRepository) {
        this.propertyRepository = propertyRepository;
        this.premiseToHuService = premiseToHuService;
        this.countryPropertyRepository = countryPropertyRepository;
    }

    public AirfreightResult doCalculation(Integer setId, Integer periodId, Premise premise, Destination destination) {

        var maxAirfreightShare = Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.AIR_SHARE, setId, premise.getCountryId()).orElseThrow().getCurrentValue());
        var overseaShare = premise.getOverseaShare().doubleValue();
        var airfreightShare = getAirfreightShare(maxAirfreightShare, overseaShare);

        var hu = premiseToHuService.createHuFromPremise(premise);

        var preCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_PRECARRIAGE, setId).orElseThrow().getCurrentValue()));
        var mainCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_MAINCARRIAGE, setId).orElseThrow().getCurrentValue()));
        var postCarriage = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_ONCARRIAGE, setId).orElseThrow().getCurrentValue()));
        var terminalFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_TERMINAL_FEE, setId).orElseThrow().getCurrentValue()));
        var preCarriageFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_HANDLING, setId).orElseThrow().getCurrentValue()));
        var customsClearanceFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_CUSTOM_FEE, setId).orElseThrow().getCurrentValue()));
        var handOverFee = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.AIR_HANDOVER_FEE, setId).orElseThrow().getCurrentValue()));

        var result = new AirfreightResult();

        var huCost = BigDecimal.valueOf(1.08).multiply(((getShippingWeight(hu).multiply(preCarriage.add(mainCarriage).add(postCarriage).add(terminalFee))).add(preCarriageFee).add(customsClearanceFee).add(handOverFee)));
        var totalCost = huCost.multiply(BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(premise.getHuUnitCount()), 2, RoundingMode.HALF_UP));
        var airfreightShareCost = totalCost.multiply(BigDecimal.valueOf(airfreightShare));

        result.setVolumetricWeight(BigDecimal.valueOf(getVolumeWeight(hu)));
        result.setWeight(BigDecimal.valueOf(getWeightInKg(hu)));
        result.setMaxAirfreightShare(BigDecimal.valueOf(maxAirfreightShare));
        result.setAirFreightShare(BigDecimal.valueOf(airfreightShare));
        result.setAnnualCost(airfreightShareCost);

        return result;

    }

    private BigDecimal getShippingWeight(PackagingDimension hu) {
        return BigDecimal.valueOf(Math.max(getWeightInKg(hu), getVolumeWeight(hu)));
    }

    private double getVolumeWeight(PackagingDimension hu) {
        return 0.0002 * getDimensionInCm(hu.getLength(), hu.getDimensionUnit()) * getDimensionInCm(hu.getWidth(), hu.getDimensionUnit()) * getDimensionInCm(hu.getHeight(), hu.getDimensionUnit());
    }

    private double getDimensionInCm(Number dimension, DimensionUnit unit) {
        return DimensionUnit.CM.convertFromMM(dimension);
    }

    private double getWeightInKg(PackagingDimension hu) {
        return WeightUnit.KG.convertFromG(hu.getWeight());
    }

    private double getAirfreightShare(double maxAirfreightShare, double overseaShare) {

        maxAirfreightShare = maxAirfreightShare*100;
        overseaShare = overseaShare*100;

        // Ensure oversea share is within valid range
        if (overseaShare < 0 || overseaShare > 100) {
            throw new IllegalArgumentException("Oversea share must be between 0 and 100");
        }

        // First segment: from (0,0) to (50, 20% of max)
        if (overseaShare <= 50) {
            // Linear interpolation: y = mx + b
            // m = (0.2 * maxAirfreightShare - 0) / (50 - 0) = 0.004 * maxAirfreightShare
            // b = 0
            return (0.004 * maxAirfreightShare * overseaShare)/100;
        }
        // Second segment: from (50, 20% of max) to (100, max)
        else {
            // Linear interpolation: y = mx + b
            // m = (maxAirfreightShare - 0.2 * maxAirfreightShare) / (100 - 50) = 0.016 * maxAirfreightShare
            // b = 0.2 * maxAirfreightShare - 50 * 0.016 * maxAirfreightShare = -0.6 * maxAirfreightShare
            return (0.016 * maxAirfreightShare * overseaShare - 0.6 * maxAirfreightShare)/100;
        }

    }

}
