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

import de.avatic.lcc.model.calculation.HandlingResult;
import de.avatic.lcc.model.db.packaging.LoadCarrierType;
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.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 HandlingCostCalculationService {


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

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

    public HandlingResult doCalculation(Integer setId, Premise premise, Destination destination, Boolean addRepackingAndDisposalCost) {

        var hu = premiseToHuService.createHuFromPremise(premise);
        return (LoadCarrierType.SLC == hu.getLoadCarrierType() ? getSLCCost(setId, destination, hu, hu.getLoadCarrierType(), addRepackingAndDisposalCost) : getLLCCost(setId, destination, hu, hu.getLoadCarrierType(), addRepackingAndDisposalCost));
    }

    private HandlingResult getSLCCost(Integer setId, Destination destination, PackagingDimension hu, LoadCarrierType loadCarrierType, boolean addRepackingAndDisposalCost) {

        var destinationHandling = destination.getHandlingCost();
        var destinationDisposal = destination.getDisposalCost();
        var destinationRepacking = destination.getRepackingCost();

        BigDecimal huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),4, RoundingMode.UP );
        BigDecimal handling = destinationHandling != null ? destinationHandling : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING, setId).orElseThrow().getCurrentValue()));
        BigDecimal release =  BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE, setId).orElseThrow().getCurrentValue()));
        BigDecimal dispatch =  BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH, setId).orElseThrow().getCurrentValue()));
        BigDecimal disposal = destinationDisposal != null ? destinationDisposal : (addRepackingAndDisposalCost ? BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL, setId).orElseThrow().getCurrentValue())) : BigDecimal.ZERO);

        BigDecimal wageFactor = BigDecimal.valueOf(Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, setId, destination.getCountryId()).orElseThrow().getCurrentValue()));
        BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING_KLT, setId).orElseThrow().getCurrentValue()));

        return new HandlingResult(LoadCarrierType.SLC,
                getRepackingCost(setId, hu, loadCarrierType, addRepackingAndDisposalCost, destinationRepacking).multiply(huAnnualAmount),
                handling.multiply(huAnnualAmount),
                destinationDisposal == null ? BigDecimal.ZERO : (disposal.multiply(huAnnualAmount)), //TODO: disposal SLC, ignore?
                huAnnualAmount.multiply((handling.add(booking).add(release).add(dispatch).add(getRepackingCost(setId, hu, loadCarrierType, addRepackingAndDisposalCost, destinationRepacking)))).multiply(wageFactor));

    }

    private BigDecimal getRepackingCost(Integer setId, PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts, BigDecimal userInput) {

        if(userInput != null)
            return userInput;

        if (!addRepackingCosts)
            return BigDecimal.ZERO;


        return switch (type) {
            case SLC ->
                    BigDecimal.valueOf(Double.parseDouble((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_S, setId).orElseThrow().getCurrentValue() : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_REPACK_M, setId).orElseThrow().getCurrentValue()));
            case LLC ->
                    BigDecimal.valueOf(Double.parseDouble(((hu.getWeight() < 15_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_S, setId) : (hu.getWeight() < 2_000_000) ? propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_M, setId) : propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_REPACK_L, setId)).orElseThrow().getCurrentValue()));
        };
    }


    private HandlingResult getLLCCost(Integer setId, Destination destination, PackagingDimension hu, LoadCarrierType type, boolean addRepackingAndDisposalCost) {

        var destinationHandling = destination.getHandlingCost();
        var destinationDisposal = destination.getDisposalCost();
        var destinationRepacking = destination.getRepackingCost();

        BigDecimal huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),4, RoundingMode.UP );
        BigDecimal handling = destinationHandling != null ? destinationHandling : BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING, setId).orElseThrow().getCurrentValue()));
        BigDecimal release =  BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE, setId).orElseThrow().getCurrentValue()));
        BigDecimal dispatch =  BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH, setId).orElseThrow().getCurrentValue()));
        BigDecimal disposal = destinationDisposal != null ? destinationDisposal : (addRepackingAndDisposalCost ? BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL, setId).orElseThrow().getCurrentValue())) : BigDecimal.ZERO);

        BigDecimal wageFactor = BigDecimal.valueOf(Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, setId, destination.getCountryId()).orElseThrow().getCurrentValue()));
        BigDecimal booking = BigDecimal.valueOf(Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING, setId).orElseThrow().getCurrentValue()));

        var annualRepacking = getRepackingCost(setId, hu, type, addRepackingAndDisposalCost, destinationRepacking).multiply(wageFactor).multiply( huAnnualAmount);
        var annualHandling = ((handling.add(dispatch).add(release)).multiply(wageFactor).multiply(huAnnualAmount)).add(booking.multiply(BigDecimal.valueOf(shippingFrequencyCalculationService.doCalculation(setId, huAnnualAmount.doubleValue()))));
        var annualDisposal = (disposal.multiply(huAnnualAmount));

        return new HandlingResult(LoadCarrierType.LLC, annualRepacking, annualHandling, annualDisposal, annualRepacking.add(annualHandling).add(annualDisposal));

    }


}
