package com.airlock.iam.flow.shared.domain.model.cronto;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.core.misc.cronto.CrontoHandler;
import com.airlock.iam.core.misc.impl.token.cronto.CrontoLetterToken;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.step.FlowCondition;
import com.airlock.iam.flow.shared.application.configuration.cronto.CrontoLetterOrderConditionConfig;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;

@LogicForConfig(CrontoLetterOrderConditionConfig.class)
public final class CrontoLetterOrderCondition implements FlowCondition {
   private static final ELogger LOG = ELoggerFactory.getLogger(CrontoLetterOrderCondition.class);
   private final Provider<PrincipalInfo> user;
   private final CrontoHandler crontoHandler;
   private final CrontoLetterOrderCondition.CrontoLetterOrderSettings settings;

   @Inject
   public CrontoLetterOrderCondition(Provider<PrincipalInfo> user, CrontoHandler crontoHandler, CrontoLetterOrderCondition.CrontoLetterOrderSettings settings) {
      this.user = user;
      this.crontoHandler = crontoHandler;
      this.settings = settings;
   }

   @Override
   public boolean isFulfilled() {
      UserId userId = this.userId();
      if (!this.crontoHandler.getAccountForUser(userId).isEmpty()) {
         Optional<CrontoLetterToken> existingLetter = this.crontoHandler.getAllLettersForUser(userId).stream().findFirst();
         if (existingLetter.isEmpty()) {
            return this.logIfFalse(!this.settings.isCrontoLetterRequired(), "user has no Cronto letter");
         }

         Instant lastOrder = existingLetter.get().getGenerationDate().toInstant();
         return this.logIfFalse(lastOrder.plus(this.settings.getMinimalLetterOrderInterval()).isBefore(Instant.now()), "last letter order is too recent");
      } else {
         return this.logIfFalse(!this.settings.isCrontoAccountRequired() && !this.settings.isCrontoLetterRequired(), "user has no Cronto account");
      }
   }

   private boolean logIfFalse(boolean result, String reason) {
      if (!result) {
         LOG.debug("User " + LogUtil.quote(this.userId()) + " is not allowed to order a Cronto activation letter because " + reason + ".");
      }

      return result;
   }

   private UserId userId() {
      return ((PrincipalInfo)this.user.get()).getKnownIdentity().orElseThrow().id();
   }

   @Override
   public String displayName() {
      return "Can Order Cronto Letter";
   }

   public static final class CrontoLetterOrderSettings {
      private final boolean crontoAccountRequired;
      private final boolean crontoLetterRequired;
      private final Duration minimalLetterOrderInterval;

      @SuppressFBWarnings(justification = "generated code")
      CrontoLetterOrderSettings(boolean crontoAccountRequired, boolean crontoLetterRequired, Duration minimalLetterOrderInterval) {
         this.crontoAccountRequired = crontoAccountRequired;
         this.crontoLetterRequired = crontoLetterRequired;
         this.minimalLetterOrderInterval = minimalLetterOrderInterval;
      }

      @SuppressFBWarnings(justification = "generated code")
      public static CrontoLetterOrderCondition.CrontoLetterOrderSettings.CrontoLetterOrderSettingsBuilder builder() {
         return new CrontoLetterOrderCondition.CrontoLetterOrderSettings.CrontoLetterOrderSettingsBuilder();
      }

      @SuppressFBWarnings(justification = "generated code")
      public boolean isCrontoAccountRequired() {
         return this.crontoAccountRequired;
      }

      @SuppressFBWarnings(justification = "generated code")
      public boolean isCrontoLetterRequired() {
         return this.crontoLetterRequired;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Duration getMinimalLetterOrderInterval() {
         return this.minimalLetterOrderInterval;
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public boolean equals(Object o) {
         if (o == this) {
            return true;
         } else if (!(o instanceof CrontoLetterOrderCondition.CrontoLetterOrderSettings other)) {
            return false;
         } else {
            if (this.crontoAccountRequired != other.crontoAccountRequired) {
               return false;
            }

            if (this.crontoLetterRequired != other.crontoLetterRequired) {
               return false;
            }

            Object this$minimalLetterOrderInterval = this.minimalLetterOrderInterval;
            Object other$minimalLetterOrderInterval = other.minimalLetterOrderInterval;
            return this$minimalLetterOrderInterval == null
               ? other$minimalLetterOrderInterval == null
               : this$minimalLetterOrderInterval.equals(other$minimalLetterOrderInterval);
         }
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public int hashCode() {
         int PRIME = 59;
         int result = 1;
         result = result * 59 + (this.crontoAccountRequired ? 79 : 97);
         result = result * 59 + (this.crontoLetterRequired ? 79 : 97);
         Object $minimalLetterOrderInterval = this.minimalLetterOrderInterval;
         return result * 59 + ($minimalLetterOrderInterval == null ? 43 : $minimalLetterOrderInterval.hashCode());
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "CrontoLetterOrderCondition.CrontoLetterOrderSettings(crontoAccountRequired="
            + this.isCrontoAccountRequired()
            + ", crontoLetterRequired="
            + this.isCrontoLetterRequired()
            + ", minimalLetterOrderInterval="
            + this.getMinimalLetterOrderInterval()
            + ")";
      }

      @SuppressFBWarnings(justification = "generated code")
      public static class CrontoLetterOrderSettingsBuilder {
         @SuppressFBWarnings(justification = "generated code")
         private boolean crontoAccountRequired;
         @SuppressFBWarnings(justification = "generated code")
         private boolean crontoLetterRequired;
         @SuppressFBWarnings(justification = "generated code")
         private Duration minimalLetterOrderInterval;

         @SuppressFBWarnings(justification = "generated code")
         CrontoLetterOrderSettingsBuilder() {
         }

         @SuppressFBWarnings(justification = "generated code")
         public CrontoLetterOrderCondition.CrontoLetterOrderSettings.CrontoLetterOrderSettingsBuilder crontoAccountRequired(boolean crontoAccountRequired) {
            this.crontoAccountRequired = crontoAccountRequired;
            return this;
         }

         @SuppressFBWarnings(justification = "generated code")
         public CrontoLetterOrderCondition.CrontoLetterOrderSettings.CrontoLetterOrderSettingsBuilder crontoLetterRequired(boolean crontoLetterRequired) {
            this.crontoLetterRequired = crontoLetterRequired;
            return this;
         }

         @SuppressFBWarnings(justification = "generated code")
         public CrontoLetterOrderCondition.CrontoLetterOrderSettings.CrontoLetterOrderSettingsBuilder minimalLetterOrderInterval(
            Duration minimalLetterOrderInterval
         ) {
            this.minimalLetterOrderInterval = minimalLetterOrderInterval;
            return this;
         }

         @SuppressFBWarnings(justification = "generated code")
         public CrontoLetterOrderCondition.CrontoLetterOrderSettings build() {
            return new CrontoLetterOrderCondition.CrontoLetterOrderSettings(
               this.crontoAccountRequired, this.crontoLetterRequired, this.minimalLetterOrderInterval
            );
         }

         @SuppressFBWarnings(justification = "generated code")
         @Override
         public String toString() {
            return "CrontoLetterOrderCondition.CrontoLetterOrderSettings.CrontoLetterOrderSettingsBuilder(crontoAccountRequired="
               + this.crontoAccountRequired
               + ", crontoLetterRequired="
               + this.crontoLetterRequired
               + ", minimalLetterOrderInterval="
               + this.minimalLetterOrderInterval
               + ")";
         }
      }
   }
}
