package com.airlock.iam.common.application.service.token.mtan;

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.common.api.domain.model.token.exception.TokenCountLimitException;
import com.airlock.iam.common.domain.model.mtan.Iak;
import com.airlock.iam.common.domain.model.mtan.MtanItemBackedIak;
import com.airlock.iam.common.domain.model.token.mtan.MtanTokenRepository;
import com.airlock.iam.log.api.signing.SigningLogger;
import java.util.Optional;

public class MtanTokenOrderService {
   private final MtanTokenRepository repository;
   private final SigningLogger signingLogger;

   public MtanTokenOrderService(SigningLogger signingLogger, MtanTokenRepository repository) {
      this.repository = repository;
      this.signingLogger = signingLogger;
   }

   public void orderIak(UserId userId) {
      if (!this.repository.canOrderIak(userId)) {
         this.signingLogger.log("Could not order IAK because token count limit is reached for user \"%s\"", new Object[]{userId});
         throw new TokenCountLimitException();
      }

      Optional<MtanItemBackedIak> existingIak = this.getIak(userId);
      MtanItemBackedIak iak;
      if (existingIak.isPresent()) {
         iak = existingIak.get();
      } else {
         iak = this.createIak(userId);
         this.signingLogger.log("Ordered IAK for user \"%s\"", new Object[]{userId});
      }

      iak.order();
      this.repository.store(userId, iak);
   }

   public void cancelIakOrder(UserId userId) {
      Optional<Iak> iakOptional = this.repository.getIak(userId);
      if (iakOptional.isPresent()) {
         Iak iak = iakOptional.get();
         iak.cancelOrder();
         if (this.repository.isIakAlsoMtanToken(iak)) {
            this.repository.store(userId, iak);
         } else {
            this.repository.delete(userId, iak);
         }

         this.signingLogger.log("Canceled IAK order for user \"%s\"", new Object[]{userId});
      }
   }

   private Optional<MtanItemBackedIak> getIak(UserId userId) {
      return this.repository.getIak(userId).map(MtanItemBackedIak.class::cast);
   }

   private MtanItemBackedIak createIak(UserId userId) {
      return (MtanItemBackedIak)this.repository.createIak(userId);
   }
}
