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

import de.avatic.lcc.calculationmodel.HandlingResult;
import de.avatic.lcc.model.packaging.LoadCarrierType;
import de.avatic.lcc.model.packaging.PackagingDimension;
import de.avatic.lcc.model.premises.Premise;
import de.avatic.lcc.model.premises.route.Destination;
import de.avatic.lcc.model.properties.CountryPropertyMappingId;
import de.avatic.lcc.model.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(Premise premise, Destination destination, Boolean addRepackingCosts) {

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

    private HandlingResult getSLCCost(Destination destination, PackagingDimension hu, LoadCarrierType loadCarrierType, boolean addRepackingCosts) {
        int huAnnualAmount = destination.getAnnualAmount() / hu.getContentUnitCount();
        double handling = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_HANDLING).orElseThrow().getCurrentValue());
        double release = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_RELEASE).orElseThrow().getCurrentValue());
        double dispatch = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.KLT_DISPATCH).orElseThrow().getCurrentValue());
        double wageFactor = Double.parseDouble(countryPropertyRepository.getByMappingIdAndCountryId(CountryPropertyMappingId.WAGE, destination.getCountryId()).orElseThrow().getCurrentValue());
        double booking = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.BOOKING).orElseThrow().getCurrentValue());

        return new HandlingResult(LoadCarrierType.SLC,
                BigDecimal.valueOf(getRepackingCost(hu, loadCarrierType, addRepackingCosts) * huAnnualAmount),
                BigDecimal.valueOf(handling*huAnnualAmount),
                BigDecimal.ZERO,
                BigDecimal.valueOf(huAnnualAmount * (handling + booking + release + dispatch + getRepackingCost(hu, loadCarrierType, addRepackingCosts)) * wageFactor));

    }

    private double getRepackingCost(PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts) {

        if (!addRepackingCosts)
            return 0;

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


    private HandlingResult getLLCCost(Destination destination, PackagingDimension hu, LoadCarrierType type, boolean addRepackingCosts) {
        double huAnnualAmount = BigDecimal.valueOf(destination.getAnnualAmount()).divide(BigDecimal.valueOf(hu.getContentUnitCount()),2, RoundingMode.UP ).doubleValue();
        double handling = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_HANDLING).orElseThrow().getCurrentValue());
        double release = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_RELEASE).orElseThrow().getCurrentValue());
        double dispatch = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.GLT_DISPATCH).orElseThrow().getCurrentValue());
        double disposal = Double.parseDouble(propertyRepository.getPropertyByMappingId(SystemPropertyMappingId.DISPOSAL).orElseThrow().getCurrentValue());

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

        var annualRepacking = BigDecimal.valueOf(getRepackingCost(hu, type, addRepackingCosts) * wageFactor * huAnnualAmount);
        var annualHandling = BigDecimal.valueOf((handling+dispatch+release)*wageFactor*huAnnualAmount + (booking * shippingFrequencyCalculationService.doCalculation(huAnnualAmount)));
        var annualDisposal = BigDecimal.valueOf(disposal * huAnnualAmount);

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

    }


}
