package com.airlock.iam.admin.domain.model.token.matrixcard;

import com.airlock.iam.admin.domain.model.token.matrixcard.generator.MatrixCardGenerator;
import com.airlock.iam.base.api.domain.model.exception.EntityNotFoundException;
import com.airlock.iam.base.api.domain.model.exception.ServiceConfigurationException;
import com.airlock.iam.base.api.domain.model.identity.Identity;
import com.airlock.iam.common.domain.model.permission.PermissionViolationException;
import com.airlock.iam.core.misc.persistency.TokenUserStructure;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

public final class MatrixCardsImpl implements MatrixCards {
   private final Optional<MatrixCardGenerator> matrixCardGenerator;
   private final Identity<MatrixCards> identity;
   private Optional<MatrixCard> currentCard;
   private Optional<MatrixCard> nextCard;
   private Optional<MatrixCardChallenges> unansweredChallenges;
   private Optional<MatrixCardOrder> order;
   private TokenUserStructure token;

   private MatrixCardsImpl(Identity<MatrixCards> identity) {
      this.currentCard = Optional.empty();
      this.nextCard = Optional.empty();
      this.unansweredChallenges = Optional.empty();
      this.order = Optional.empty();
      this.matrixCardGenerator = Optional.empty();
      this.identity = identity;
   }

   public static MatrixCardsImpl.MatrixCardsImplBuilder builder(TokenUserStructure token, Identity<MatrixCards> identity) {
      return new MatrixCardsImpl(identity).toBuilder().token(token);
   }

   public Identity<MatrixCards> getId() {
      return this.identity;
   }

   @Override
   public Optional<Date> getLatestDeliveryDate() {
      return Optional.ofNullable(this.token.getDeliveryDate());
   }

   @Override
   public boolean hasCardWithId(Identity<MatrixCard> matrixCardId) {
      return this.currentCard.isPresent() && this.currentCard.get().hasId(matrixCardId) || this.nextCard.isPresent() && this.nextCard.get().hasId(matrixCardId);
   }

   @Override
   public MatrixCards resetUnansweredChallenges() {
      this.unansweredChallenges = Optional.empty();
      return this;
   }

   @Override
   public MatrixCards removeMatrixCard(Identity<MatrixCard> matrixCardId) {
      if (this.currentCard.isPresent() && this.currentCard.get().hasId(matrixCardId)) {
         this.currentCard = this.nextCard;
         this.nextCard = Optional.empty();
         return this;
      } else if (this.nextCard.isPresent() && this.nextCard.get().hasId(matrixCardId)) {
         this.nextCard = Optional.empty();
         return this;
      } else {
         throw new EntityNotFoundException(String.format("Did not find matrix card with ID '%s'", matrixCardId));
      }
   }

   @Override
   public MatrixCards order(String orderingUser) {
      this.order = Optional.of(new MatrixCardOrder(new Date(), orderingUser));
      return this;
   }

   @Override
   public MatrixCards cancelOrder() {
      this.order = Optional.empty();
      return this;
   }

   @Override
   public MatrixCards generateMatrixCard() {
      MatrixCardGenerator generator = (MatrixCardGenerator)ServiceConfigurationException.checkPresent(
         this.matrixCardGenerator, "No matrix card generator configured"
      );
      PermissionViolationException.checkTrue(this.currentCard.isEmpty() || this.nextCard.isEmpty(), "Only two matrix cards are allowed");
      MatrixCard newMatrixCard = generator.generate(this);
      if (this.currentCard.isEmpty()) {
         this.currentCard = Optional.of(newMatrixCard);
      } else {
         this.nextCard = Optional.of(newMatrixCard);
      }

      this.token.setDeliveryDate(new Date());
      return this;
   }

   @Override
   public List<MatrixCard> getAllMatrixCards() {
      List<MatrixCard> cards = new LinkedList<>();
      this.currentCard.ifPresent(card -> cards.add(card));
      this.nextCard.ifPresent(card -> cards.add(card));
      return cards;
   }

   @SuppressFBWarnings(justification = "generated code")
   public MatrixCardsImpl.MatrixCardsImplBuilder toBuilder() {
      return new MatrixCardsImpl.MatrixCardsImplBuilder()
         .matrixCardGenerator(this.matrixCardGenerator)
         .identity(this.identity)
         .currentCard(this.currentCard)
         .nextCard(this.nextCard)
         .unansweredChallenges(this.unansweredChallenges)
         .order(this.order)
         .token(this.token);
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<MatrixCardGenerator> getMatrixCardGenerator() {
      return this.matrixCardGenerator;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Identity<MatrixCards> getIdentity() {
      return this.identity;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<MatrixCard> getCurrentCard() {
      return this.currentCard;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<MatrixCard> getNextCard() {
      return this.nextCard;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<MatrixCardChallenges> getUnansweredChallenges() {
      return this.unansweredChallenges;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<MatrixCardOrder> getOrder() {
      return this.order;
   }

   @SuppressFBWarnings(justification = "generated code")
   public TokenUserStructure getToken() {
      return this.token;
   }

   @SuppressFBWarnings(justification = "generated code")
   public MatrixCardsImpl(
      Optional<MatrixCardGenerator> matrixCardGenerator,
      Identity<MatrixCards> identity,
      Optional<MatrixCard> currentCard,
      Optional<MatrixCard> nextCard,
      Optional<MatrixCardChallenges> unansweredChallenges,
      Optional<MatrixCardOrder> order,
      TokenUserStructure token
   ) {
      this.matrixCardGenerator = matrixCardGenerator;
      this.identity = identity;
      this.currentCard = currentCard;
      this.nextCard = nextCard;
      this.unansweredChallenges = unansweredChallenges;
      this.order = order;
      this.token = token;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class MatrixCardsImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Optional<MatrixCardGenerator> matrixCardGenerator;
      @SuppressFBWarnings(justification = "generated code")
      private Identity<MatrixCards> identity;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<MatrixCard> currentCard;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<MatrixCard> nextCard;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<MatrixCardChallenges> unansweredChallenges;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<MatrixCardOrder> order;
      @SuppressFBWarnings(justification = "generated code")
      private TokenUserStructure token;

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

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCardsImpl.MatrixCardsImplBuilder matrixCardGenerator(Optional<MatrixCardGenerator> matrixCardGenerator) {
         this.matrixCardGenerator = matrixCardGenerator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCardsImpl.MatrixCardsImplBuilder identity(Identity<MatrixCards> identity) {
         this.identity = identity;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCardsImpl.MatrixCardsImplBuilder currentCard(Optional<MatrixCard> currentCard) {
         this.currentCard = currentCard;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCardsImpl.MatrixCardsImplBuilder nextCard(Optional<MatrixCard> nextCard) {
         this.nextCard = nextCard;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCardsImpl.MatrixCardsImplBuilder unansweredChallenges(Optional<MatrixCardChallenges> unansweredChallenges) {
         this.unansweredChallenges = unansweredChallenges;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCardsImpl.MatrixCardsImplBuilder order(Optional<MatrixCardOrder> order) {
         this.order = order;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCardsImpl.MatrixCardsImplBuilder token(TokenUserStructure token) {
         this.token = token;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MatrixCardsImpl build() {
         return new MatrixCardsImpl(this.matrixCardGenerator, this.identity, this.currentCard, this.nextCard, this.unansweredChallenges, this.order, this.token);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "MatrixCardsImpl.MatrixCardsImplBuilder(matrixCardGenerator="
            + this.matrixCardGenerator
            + ", identity="
            + this.identity
            + ", currentCard="
            + this.currentCard
            + ", nextCard="
            + this.nextCard
            + ", unansweredChallenges="
            + this.unansweredChallenges
            + ", order="
            + this.order
            + ", token="
            + this.token
            + ")";
      }
   }
}
