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

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.common.api.domain.model.persistency.exception.PersisterException;
import com.airlock.iam.core.misc.cronto.CrontoHandler;
import com.airlock.iam.core.misc.impl.token.cronto.CrontoLetterToken;
import com.airlock.iam.core.misc.token.Token;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;

public final class CrontoOrderAdditionalLetterService {
   public static final String USER_CAN_ORDER_ADDITIONAL_LETTER = "userCanOrderAdditionalLetter";
   public static final String MINIMAL_NEW_LETTER_INTERVAL_IN_HOURS = "minimalNewLetterIntervalInHours";
   public static final String NORMAL_ORDER_OPTIONS = "";
   private static final ELogger LOG = ELoggerFactory.getLogger(CrontoOrderAdditionalLetterService.class);
   private final CrontoHandler crontoHandler;
   private final boolean userCanOrderAdditionalLetter;
   private final Duration minimalNewLetterIntervalInHours;
   private final UserId userId;

   public CrontoOrderAdditionalLetterService.OrderResult orderAdditionalLetter() throws PersisterException {
      CrontoOrderAdditionalLetterService.OrderResult precheckOrder = this.canUserOrderAdditionalActivationLetter();
      if (CrontoOrderAdditionalLetterService.OrderResult.OK == precheckOrder) {
         this.crontoHandler.deleteAllLetters(this.userId);
         CrontoLetterToken letter = this.crontoHandler.createActivationLetter(this.userId);
         this.crontoHandler.orderLetterPrinting(letter, this.userId);
      }

      return precheckOrder;
   }

   private CrontoOrderAdditionalLetterService.OrderResult canUserOrderAdditionalActivationLetter() {
      if (!this.userCanOrderAdditionalLetter) {
         return CrontoOrderAdditionalLetterService.OrderResult.NOT_CONFIGURED;
      } else {
         boolean hasCrontoAccount = this.crontoHandler.getAccountForUser(this.userId).isPresent();
         if (!hasCrontoAccount) {
            LOG.debug("Cannot order additional letter because no Cronto account available.");
            return CrontoOrderAdditionalLetterService.OrderResult.NO_CRONTO_ACCOUNT;
         } else {
            boolean hasActivationLetter = this.existsActivationLetter();
            if (!hasActivationLetter) {
               LOG.debug("User has no activation letter yet");
               return CrontoOrderAdditionalLetterService.OrderResult.NO_PREVIOUS_ACTIVATION_LETTER;
            } else {
               boolean existsTooRecentLetter = this.existsActivationLetterWithTooRecentGenerationDate();
               if (existsTooRecentLetter) {
                  LOG.debug("Cannot order additional Cronto activation letter because too recent letter exists");
                  return CrontoOrderAdditionalLetterService.OrderResult.LAST_PRINTING_DATE_TOO_RECENT;
               } else {
                  LOG.debug("Can order additional letter.");
                  return CrontoOrderAdditionalLetterService.OrderResult.OK;
               }
            }
         }
      }
   }

   private boolean existsActivationLetterWithTooRecentGenerationDate() {
      Instant now = Instant.now();
      Instant referencePoint = now.minus(this.minimalNewLetterIntervalInHours);
      return this.crontoHandler
         .getAllLettersForUser(this.userId)
         .stream()
         .map(Token::getGenerationDate)
         .filter(Objects::nonNull)
         .anyMatch(date -> date.toInstant().isAfter(referencePoint));
   }

   private boolean existsActivationLetter() {
      return !this.crontoHandler.getAllLettersForUser(this.userId).isEmpty();
   }

   @SuppressFBWarnings(justification = "generated code")
   CrontoOrderAdditionalLetterService(
      CrontoHandler crontoHandler, boolean userCanOrderAdditionalLetter, Duration minimalNewLetterIntervalInHours, UserId userId
   ) {
      this.crontoHandler = crontoHandler;
      this.userCanOrderAdditionalLetter = userCanOrderAdditionalLetter;
      this.minimalNewLetterIntervalInHours = minimalNewLetterIntervalInHours;
      this.userId = userId;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static CrontoOrderAdditionalLetterService.CrontoOrderAdditionalLetterServiceBuilder builder() {
      return new CrontoOrderAdditionalLetterService.CrontoOrderAdditionalLetterServiceBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class CrontoOrderAdditionalLetterServiceBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private CrontoHandler crontoHandler;
      @SuppressFBWarnings(justification = "generated code")
      private boolean userCanOrderAdditionalLetter;
      @SuppressFBWarnings(justification = "generated code")
      private Duration minimalNewLetterIntervalInHours;
      @SuppressFBWarnings(justification = "generated code")
      private UserId userId;

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

      @SuppressFBWarnings(justification = "generated code")
      public CrontoOrderAdditionalLetterService.CrontoOrderAdditionalLetterServiceBuilder crontoHandler(CrontoHandler crontoHandler) {
         this.crontoHandler = crontoHandler;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoOrderAdditionalLetterService.CrontoOrderAdditionalLetterServiceBuilder userCanOrderAdditionalLetter(boolean userCanOrderAdditionalLetter) {
         this.userCanOrderAdditionalLetter = userCanOrderAdditionalLetter;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoOrderAdditionalLetterService.CrontoOrderAdditionalLetterServiceBuilder minimalNewLetterIntervalInHours(
         Duration minimalNewLetterIntervalInHours
      ) {
         this.minimalNewLetterIntervalInHours = minimalNewLetterIntervalInHours;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoOrderAdditionalLetterService.CrontoOrderAdditionalLetterServiceBuilder userId(UserId userId) {
         this.userId = userId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoOrderAdditionalLetterService build() {
         return new CrontoOrderAdditionalLetterService(this.crontoHandler, this.userCanOrderAdditionalLetter, this.minimalNewLetterIntervalInHours, this.userId);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "CrontoOrderAdditionalLetterService.CrontoOrderAdditionalLetterServiceBuilder(crontoHandler="
            + this.crontoHandler
            + ", userCanOrderAdditionalLetter="
            + this.userCanOrderAdditionalLetter
            + ", minimalNewLetterIntervalInHours="
            + this.minimalNewLetterIntervalInHours
            + ", userId="
            + this.userId
            + ")";
      }
   }

   public enum OrderResult {
      OK,
      NOT_CONFIGURED,
      NO_CRONTO_ACCOUNT,
      NO_PREVIOUS_ACTIVATION_LETTER,
      LAST_PRINTING_DATE_TOO_RECENT;
   }
}
