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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.domain.model.mapification.LazySupplier;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.flow.shared.api.domain.model.session.UserDependentFlowSession;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;

public final class LazyUserSession {
   private final Optional<UserId> initialUserId;
   private final Optional<LazySupplier<MapifiedValues, UserSessionImpl>> supplier;

   public UserSessionImpl getUserSession() {
      return this.supplier.orElseThrow(() -> new FailureException("No User Session available")).get();
   }

   public Optional<UserId> getUserId() {
      if (this.hasBeenSupplied()) {
         UserSessionImpl userSession = this.supplier.orElseThrow().get();
         return userSession.userId()
            .or(
               () -> userSession.getCurrentFlowSession()
                  .filter(UserDependentFlowSession.class::isInstance)
                  .map(UserDependentFlowSession.class::cast)
                  .flatMap(UserDependentFlowSession::getUserIdentity)
            )
            .map(UserIdentity::id);
      } else {
         return this.initialUserId;
      }
   }

   public boolean hasBeenSupplied() {
      return this.supplier.isPresent() && this.supplier.get().hasBeenSupplied();
   }

   public static LazyUserSession createUnsuppliable() {
      return new LazyUserSession(Optional.empty(), Optional.empty());
   }

   public static LazyUserSession createLazyUserSession(Optional<UserId> initialUserId, LazySupplier<MapifiedValues, UserSessionImpl> supplier) {
      return new LazyUserSession(initialUserId, Optional.of(supplier));
   }

   public static LazyUserSession alreadySupplied(UserSessionImpl userSession) {
      return new LazyUserSession(Optional.empty(), Optional.of(LazySupplier.alreadySupplied(userSession)));
   }

   @SuppressFBWarnings(justification = "generated code")
   private LazyUserSession(Optional<UserId> initialUserId, Optional<LazySupplier<MapifiedValues, UserSessionImpl>> supplier) {
      this.initialUserId = initialUserId;
      this.supplier = supplier;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<LazySupplier<MapifiedValues, UserSessionImpl>> getSupplier() {
      return this.supplier;
   }
}
