package de.avatic.lcc.service.configuration;

import de.avatic.lcc.dto.configuration.rates.StagedRatesDTO;
import de.avatic.lcc.model.db.properties.SystemPropertyMappingId;
import de.avatic.lcc.model.db.rates.ValidityPeriod;
import de.avatic.lcc.repositories.rates.ContainerRateRepository;
import de.avatic.lcc.repositories.rates.MatrixRateRepository;
import de.avatic.lcc.repositories.rates.ValidityPeriodRepository;
import de.avatic.lcc.service.access.PropertyService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;

/**
 * A service class responsible for approving or verifying rate drafts.
 * Provides functionalities to check for unapproved rate drafts and
 * apply these drafts when necessary.
 */
@Service
public class RateApprovalService {


    private final ValidityPeriodRepository validityPeriodRepository;
    private final PropertyService propertyService;
    private final MatrixRateRepository matrixRateRepository;
    private final ContainerRateRepository containerRateRepository;

    public RateApprovalService(ValidityPeriodRepository validityPeriodRepository, PropertyService propertyService, MatrixRateRepository matrixRateRepository, ContainerRateRepository containerRateRepository) {
        this.validityPeriodRepository = validityPeriodRepository;
        this.propertyService = propertyService;
        this.matrixRateRepository = matrixRateRepository;
        this.containerRateRepository = containerRateRepository;
    }

    /**
     * Checks if there are any staged (draft) rates that have not yet been approved.
     *
     * @return {@code true} if rate drafts exist, {@code false} otherwise.
     */
    public StagedRatesDTO getStagedRateDTO() {

        var optValidPeriod = validityPeriodRepository.getValidPeriod();
        StagedRatesDTO stagedRatesDTO = new StagedRatesDTO();

        stagedRatesDTO.setExpires(optValidPeriod.map(this::expires).orElse(null));
        stagedRatesDTO.setStagedChanges(validityPeriodRepository.hasRateDrafts());

        return stagedRatesDTO;
    }

    private Integer expires(ValidityPeriod validPeriod) {
        Optional<Integer> validityProperty = propertyService.getProperty(SystemPropertyMappingId.VALID_DAYS);

        if (validityProperty.isPresent()) {
            var validDays = validityProperty.get() * (validPeriod.getRenewals() + 1);
            return Math.toIntExact(Duration.between(LocalDateTime.now(), validPeriod.getStartDate().plusDays(validDays)).toDays());

        }

        return null;
    }

    private Integer getRenewalIncrease() {
        Optional<Integer> validityProperty = propertyService.getProperty(SystemPropertyMappingId.VALID_DAYS);
        var optValidPeriod = validityPeriodRepository.getValidPeriod();

        if (validityProperty.isPresent() && optValidPeriod.isPresent()) {
            var validPeriod = optValidPeriod.get();
            var validDays = validityProperty.get() * (validPeriod.getRenewals() + 1);

            var expiresIn = Math.toIntExact(Duration.between(LocalDateTime.now(), validPeriod.getStartDate().plusDays(validDays)).toDays()) - 10;

            if (expiresIn < 0) {
                return ((-1 * expiresIn) / validDays) + 1;
            }
            return 1;
        }

        return null;
    }

    /**
     * Approves and applies all staged (draft) rates.
     */
    @Transactional
    public void approveRateDrafts() {

        var hasContainerDrafts = validityPeriodRepository.hasContainerRateDrafts();
        var hasMatrixDrafts = validityPeriodRepository.hasMatrixRateDrafts();

        if (hasContainerDrafts || hasMatrixDrafts) {

            if (hasContainerDrafts && !hasMatrixDrafts)
                matrixRateRepository.copyCurrentToDraft();
            else if (!hasContainerDrafts && hasMatrixDrafts)
                containerRateRepository.copyCurrentToDraft();

            validityPeriodRepository.applyDraft();
        } else {
            Integer increase = getRenewalIncrease();

            if (increase != null) {
                validityPeriodRepository.increaseRenewal(increase);
            }
        }
    }
}
