package com.airlock.iam.factor.domain.model.airlock2fa;

import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FADeviceId;
import com.airlock.iam.core.misc.authen.AuthenticationSession;
import com.airlock.iam.factor.domain.model.airlock2fa.futurae.Airlock2FAFuturaeAuthSession;
import com.airlock.iam.factor.domain.model.airlock2fa.qrcode.Airlock2FAQrCodeImage;
import com.google.common.base.Preconditions;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;

public class Airlock2FAAuthenticationSession implements AuthenticationSession {
   private static final Airlock2FAFactorSet SUPPORTED_FACTORS = Airlock2FAFactorSet.of(
      Airlock2FAFactor.ONE_TOUCH, Airlock2FAFactor.OFFLINE_QR_CODE, Airlock2FAFactor.PASSCODE
   );
   private Optional<Airlock2FAUser> airlock2FAUser = Optional.empty();
   private Airlock2FAFactorSet possibleFactors = Airlock2FAFactorSet.of();
   private Optional<Airlock2FAFactor> factor = Optional.empty();
   private Airlock2FADevices devices;
   private Optional<Airlock2FAFuturaeAuthSession> futuraeSession = Optional.empty();
   private Optional<Airlock2FAQrCodeImage> qrCodeImage = Optional.empty();
   private boolean futuraeSessionCanceled;

   public void setAirlock2FAUser(Airlock2FAUser airlock2FAUser) {
      this.airlock2FAUser = Optional.of(airlock2FAUser);
   }

   public void setPossibleFactors(Airlock2FAFactorSet possibleFactors) {
      Preconditions.checkState(SUPPORTED_FACTORS.containsAll(possibleFactors));
      this.possibleFactors = possibleFactors;
   }

   public void setFactor(Airlock2FAFactor factor) {
      this.factor = Optional.of(factor);
   }

   public void setDevices(Airlock2FADevices devices, Airlock2FAPayloadEncryptionSettings payloadEncryptionSettings) {
      Preconditions.checkState(devices.stream().allMatch(d -> d.isCapableOfAtLeastOneFactor(this.possibleFactors, payloadEncryptionSettings)));
      this.devices = devices;
   }

   public void setDevice(Airlock2FAAuthDevice newDevice, Airlock2FAPayloadEncryptionSettings payloadEncryptionSettings) {
      this.setDevices(Airlock2FADevices.of(newDevice), payloadEncryptionSettings);
      this.setPossibleFactors(this.possibleFactors.intersect(newDevice.getSupportedFactors(payloadEncryptionSettings)));
   }

   public Airlock2FADeviceId getOnlyDeviceIdOrThrow() {
      return this.devices.onlyDeviceOrThrow().getId();
   }

   public void setFuturaeSession(Airlock2FASessionId sessionId) {
      Preconditions.checkState(this.airlock2FAUser.isPresent(), "Airlock 2FA account ID expected to be set at this point");
      Preconditions.checkState(this.factor.isPresent(), "Airlock 2FA factor expected to be set at this point");
      Airlock2FAFuturaeAuthSession session = new Airlock2FAFuturaeAuthSession(this.airlock2FAUser.get().getAccountId(), sessionId, this.factor.get());
      this.setFuturaeSession(session);
   }

   private void setFuturaeSession(Airlock2FAFuturaeAuthSession futuraeSession) {
      this.futuraeSession = Optional.of(futuraeSession);
      this.futuraeSessionCanceled = false;
   }

   public void cancelFuturaeSession() {
      this.futuraeSession = Optional.empty();
      this.futuraeSessionCanceled = true;
   }

   public void setQrCodeImage(Airlock2FAQrCodeImage qrCodeImage) {
      this.qrCodeImage = Optional.of(qrCodeImage);
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAUser> getAirlock2FAUser() {
      return this.airlock2FAUser;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Airlock2FAFactorSet getPossibleFactors() {
      return this.possibleFactors;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAFactor> getFactor() {
      return this.factor;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Airlock2FADevices getDevices() {
      return this.devices;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAFuturaeAuthSession> getFuturaeSession() {
      return this.futuraeSession;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAQrCodeImage> getQrCodeImage() {
      return this.qrCodeImage;
   }

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