package com.airlock.iam.common.domain.model.valueprovider;

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.common.api.domain.model.valueprovider.ValueProvider;
import com.airlock.iam.common.application.configuration.valueprovider.DateAndTimeWithOffsetValueProviderConfig;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Date;
import java.util.Optional;
import java.util.function.BiFunction;
import org.apache.commons.lang3.time.DateUtils;

@LogicForConfig(DateAndTimeWithOffsetValueProviderConfig.class)
public final class DateAndTimeWithOffsetValueProvider implements ValueProvider<Date> {
   private final Provider<DateAndTimeWithOffsetValueProviderSettings> settings;
   private final Provider<ValueProvider<Date>> startingDateAndTimeProvider;
   private final Provider<ValueProvider<Integer>> offsetAmountProvider;

   @Inject
   DateAndTimeWithOffsetValueProvider(
      Provider<DateAndTimeWithOffsetValueProviderSettings> settings,
      Provider<ValueProvider<Date>> startingDateAndTimeProvider,
      Provider<ValueProvider<Integer>> offsetAmountProvider
   ) {
      this.settings = settings;
      this.startingDateAndTimeProvider = startingDateAndTimeProvider;
      this.offsetAmountProvider = offsetAmountProvider;
   }

   public Date getValue() {
      return additionFunction(((DateAndTimeWithOffsetValueProviderSettings)this.settings.get()).unit())
         .apply(this.getStartingDateAndTime(), this.getOffsetAmount());
   }

   private Integer getOffsetAmount() {
      return (Integer)Optional.ofNullable((Integer)((ValueProvider)this.offsetAmountProvider.get()).getValue())
         .orElseThrow(() -> new ViolationException("Must specify an offset amount."));
   }

   private Date getStartingDateAndTime() {
      return (Date)Optional.ofNullable((Date)((ValueProvider)this.startingDateAndTimeProvider.get()).getValue())
         .orElseThrow(() -> new ViolationException("Must specify a starting date."));
   }

   private static BiFunction<Date, Integer, Date> additionFunction(DateAndTimeWithOffsetValueProviderConfig.DateAndTimeUnit unit) {
      return switch (unit) {
         case YEARS -> DateUtils::addYears;
         case MONTHS -> DateUtils::addMonths;
         case DAYS -> DateUtils::addDays;
         case HOURS -> DateUtils::addHours;
         case MINUTES -> DateUtils::addMinutes;
         case SECONDS -> DateUtils::addSeconds;
         case MILLISECONDS -> DateUtils::addMilliseconds;
      };
   }
}
