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

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.validation.GenericError;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.api.domain.model.validation.Validator;
import com.airlock.iam.base.api.domain.model.validation.ValidatorError;
import com.airlock.iam.base.api.domain.model.validation.info.DateRangeValidationInfo;
import com.airlock.iam.base.api.domain.model.validation.info.ValidationInfo;
import com.airlock.iam.base.misc.time.TimeProvider;
import com.airlock.iam.common.application.configuration.validation.DefaultDateValidatorConfig;
import com.airlock.iam.common.infrastructure.LocalDateRange;
import jakarta.inject.Inject;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;

@LogicForConfig(DefaultDateValidatorConfig.class)
public final class DefaultDateValidator implements Validator<LocalDate> {
   private final DefaultDateValidatorConfig config;
   private final TimeProvider timeProvider;

   @Inject
   public DefaultDateValidator(DefaultDateValidatorConfig config, TimeProvider timeProvider) {
      this.config = config;
      this.timeProvider = timeProvider;
   }

   public ValidationResult validate(LocalDate input) {
      if (input == null) {
         return ValidationResult.invalid(ValidatorError.validatorError(GenericError.REQUIRED));
      }

      LocalDateRange range = this.createRange();
      return range.validateInInclusiveRange(input);
   }

   private LocalDateRange createRange() {
      LocalDateRange.LocalDateRangeBuilder result = LocalDateRange.builder();
      LocalDate today = LocalDate.ofInstant(this.timeProvider.getCurrentInstant(), ZoneId.systemDefault());
      this.lowerBound(today).ifPresent(result::fromDate);
      this.upperBound(today).ifPresent(result::toDate);
      return result.build();
   }

   private Optional<LocalDate> lowerBound(LocalDate today) {
      Optional<Period> period = this.config.getMinRelative();
      return period.isPresent() ? Optional.of(today.plus(period.get())) : this.config.getMinDate();
   }

   private Optional<LocalDate> upperBound(LocalDate today) {
      Optional<Period> period = this.config.getMaxRelative();
      return period.isPresent() ? Optional.of(today.plus(period.get())) : this.config.getMaxDate();
   }

   @Override
   public List<ValidationInfo> getInfo() {
      LocalDate today = LocalDate.ofInstant(this.timeProvider.getCurrentInstant(), ZoneId.systemDefault());
      Optional<LocalDate> min = this.lowerBound(today);
      Optional<LocalDate> max = this.upperBound(today);
      if (min.isEmpty() && max.isEmpty()) {
         return List.of();
      }

      ValidationInfo info;
      if (min.isPresent() && max.isPresent()) {
         info = DateRangeValidationInfo.createForRange(min.get(), max.get());
      } else if (min.isPresent()) {
         info = DateRangeValidationInfo.createForMin(min.get());
      } else {
         info = DateRangeValidationInfo.createForMax(max.get());
      }

      return List.of(info);
   }
}
