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

import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FAAccountId;
import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FADeviceId;
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.domain.model.mapification.MapificationUtils;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.common.api.domain.model.authentication.factor.AuthenticationFactorDetail;
import com.airlock.iam.core.domain.model.authentication.factor.KnownAuthenticationFactorDetail;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.elog.api.LogTypes;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAAuthDevice;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAAuthDeviceMapifier;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAAuthLogData;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAAuthTokenId;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FADeviceCapability;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FADeviceSettings;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FADevices;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAFactor;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAFactorPriority;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAFactorSet;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAMultiNumberedChallenge;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAPayloadEncryptionSettings;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FARemainingFactorsAndDevices;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FASessionId;
import com.airlock.iam.factor.domain.model.airlock2fa.futurae.Airlock2FAFuturaeAuthSession;
import com.airlock.iam.factor.domain.model.airlock2fa.futurae.Airlock2FAFuturaeAuthSessionMapifier;
import com.airlock.iam.factor.domain.model.airlock2fa.qrcode.Airlock2FAExchangeTokenData;
import com.airlock.iam.factor.domain.service.airlock2fa.Airlock2FAFactorInfoMapper;
import com.airlock.iam.flow.api.domain.model.step.StepSession;
import com.airlock.iam.flow.shared.domain.model.mapification.Airlock2FAOnlineApprovalSessionMapifier;
import com.airlock.iam.flow.shared.domain.model.mapification.Airlock2FARemainingFactorsAndDevicesMapifier;
import com.airlock.iam.flow.shared.domain.model.session.PollingResultCache;
import com.google.common.base.Preconditions;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

public final class Airlock2FAApprovalStepSession implements StepSession {
   private static final ELogger LOG = ELoggerFactory.getLogger(Airlock2FAApprovalStepSession.class);
   private static final String ACCOUNT_ID_KEY = "accountId";
   private static final String ONLINE_SESSION_KEY = "onlineSession";
   private static final String OFFLINE_SESSION_KEY = "offlineSession";
   private static final String ONLINE_QR_CODE_CHALLENGE_KEY = "onlineQrCode";
   private static final String MOBILE_ONLY_CHALLENGE_KEY = "mobileOnly";
   private static final String OFFLINE_QR_CODE_CHALLENGE_KEY = "offlineQrCode";
   private static final String SESSION_EXCHANGE_TOKENS_KEY = "sessionExchangeTokens";
   private static final String SESSION_START_TIME_KEY = "sessionStartTime";
   private static final String ALLOWED_DEVICES_KEY = "allowedDevices";
   private static final String REMAINING_FACTORS_AND_DEVICES_KEY = "remainingFactorsAndDevices";
   private static final String BYPASSED_FACTOR_KEY = "bypassed";
   private static final String MULTI_NUMBERED_CHALLENGE_KEY = "multiNumberedChallenge";
   private Optional<Airlock2FAAccountId> accountId = Optional.empty();
   private Optional<Airlock2FAOnlineApprovalSession> onlineSession = Optional.empty();
   private Optional<Airlock2FAFuturaeAuthSession> offlineSession = Optional.empty();
   private Optional<Airlock2FAOnlineQrCodeChallenge> onlineQrCodeChallenge = Optional.empty();
   private Optional<Airlock2FAMobileOnlyChallenge> mobileOnlyChallenge = Optional.empty();
   private Optional<Airlock2FAOfflineQrCodeChallenge> offlineQrCodeChallenge = Optional.empty();
   private List<Airlock2FAExchangeTokenData> sessionExchangeTokens = Collections.emptyList();
   private Optional<Instant> sessionStartTime = Optional.empty();
   private Airlock2FADevices allowedDevices = Airlock2FADevices.of();
   private Airlock2FARemainingFactorsAndDevices remainingFactorsAndDevices = Airlock2FARemainingFactorsAndDevices.remainingFactorsAndDevices();
   private Optional<Airlock2FAFactor> bypasedFactor = Optional.empty();
   private final PollingResultCache pollingCache;
   private Optional<Airlock2FAMultiNumberedChallenge> multiNumberedChallenge = Optional.empty();

   public static Airlock2FAApprovalStepSession emptySession() {
      return new Airlock2FAApprovalStepSession(new PollingResultCache());
   }

   public void initializeFactorsAndDevices(Airlock2FADeviceSettings deviceSettings, Airlock2FAFactorSet factorsFromBackend, Airlock2FADevices devices) {
      Airlock2FAPayloadEncryptionSettings payloadEncryptionSettings = deviceSettings.payloadEncryptionSettings();
      if (deviceSettings.factorPriority().contains(Airlock2FAFactor.OFFLINE_QR_CODE) && payloadEncryptionSettings.requireEncryptedPayloadForHwTokens()) {
         this.logWarningIfUserHasQrCodeScanningHwToken(devices);
      }

      this.remainingFactorsAndDevices = Airlock2FARemainingFactorsAndDevices.remainingFactorsAndDevices(deviceSettings, devices);
      this.remainingFactorsAndDevices.intersectFactors(factorsFromBackend.factors(), payloadEncryptionSettings);
      this.allowedDevices = this.remainingFactorsAndDevices.remainingDevices();
   }

   private void logWarningIfUserHasQrCodeScanningHwToken(Airlock2FADevices devices) {
      devices.stream()
         .filter(device -> device.isHardwareToken() && device.getCapabilities().contains(Airlock2FADeviceCapability.QR_CODE))
         .forEach(
            device -> LOG.warn(
               LogTypes.CONFIGURATION_ERROR,
               "Factor %s is not allowed for hardware device with id \"%s\" because payload encryption is enabled and unencrypted payload for HW tokens is disabled."
                  .formatted(Airlock2FAFactor.OFFLINE_QR_CODE.getDisplayName(), device.getId())
            )
         );
   }

   public void setBypassedFactor(Airlock2FAFactor factor) {
      this.bypasedFactor = Optional.ofNullable(factor);
   }

   public boolean requiresDeviceSelection(boolean pushToAllEnabled) {
      return this.remainingFactorsAndDevices.requiresDeviceSelection(pushToAllEnabled);
   }

   public Airlock2FADevices remainingDevices() {
      return this.remainingFactorsAndDevices.remainingDevices();
   }

   public Airlock2FADevices cooldownDevices() {
      return this.remainingFactorsAndDevices.devicesInCooldown();
   }

   public void setAccountId(Airlock2FAAccountId id) {
      this.accountId = Optional.of(id);
   }

   public void setOnlineSession(Airlock2FASessionId sessionId, Airlock2FAFactor factor) {
      Preconditions.checkState(this.accountId.isPresent(), "Airlock 2FA account ID expected to be set at this point");
      this.onlineSession = Optional.of(
         Airlock2FAOnlineApprovalSession.onlineApprovalSession(new Airlock2FAFuturaeAuthSession(this.accountId.get(), sessionId, factor))
      );
   }

   public void setOnlineQrCodeChallenge(Airlock2FAOnlineQrCodeChallenge challenge) {
      this.onlineQrCodeChallenge = Optional.of(challenge);
   }

   public void setMobileOnlyChallenge(Airlock2FAMobileOnlyChallenge challenge) {
      this.mobileOnlyChallenge = Optional.of(challenge);
   }

   public void setSessionExchangeTokens(List<Airlock2FAExchangeTokenData> sessionExchangeTokens) {
      this.sessionExchangeTokens = sessionExchangeTokens;
   }

   public void setSessionStartTime(Instant startTime) {
      this.sessionStartTime = Optional.of(startTime);
   }

   public void setOfflineSession(Airlock2FASessionId sessionId) {
      Preconditions.checkState(this.accountId.isPresent(), "Airlock 2FA account ID expected to be set at this point");
      this.offlineSession = Optional.of(new Airlock2FAFuturaeAuthSession(this.accountId.get(), sessionId, Airlock2FAFactor.OFFLINE_QR_CODE));
   }

   public Airlock2FAFactorPriority getRemainingFactors() {
      return this.remainingFactorsAndDevices.factors();
   }

   public Optional<Airlock2FAFactor> topFactor() {
      return this.remainingFactorsAndDevices.topFactor();
   }

   public boolean topFactorEquals(Airlock2FAFactor factor) {
      return this.topFactor().map(factor::equals).orElse(false);
   }

   public Airlock2FAAccountId getAccountIdOrThrow() {
      return this.accountId.orElseThrow();
   }

   public boolean isAccountIdPresent() {
      return this.accountId.isPresent();
   }

   public boolean isOnlineSessionPresent() {
      return this.onlineSession.isPresent();
   }

   public boolean isOfflineSessionPresent() {
      return this.offlineSession.isPresent();
   }

   public Airlock2FAFuturaeAuthSession getOnlineSessionOrThrow() {
      return this.onlineSession.orElseThrow().getSession();
   }

   public Airlock2FAFuturaeAuthSession getOfflineSessionOrThrow() {
      return this.offlineSession.orElseThrow();
   }

   public void clearOfflineSession() {
      this.offlineSession = Optional.empty();
   }

   public void clearOnlineSession() {
      this.onlineSession = Optional.empty();
      this.onlineQrCodeChallenge = Optional.empty();
      this.mobileOnlyChallenge = Optional.empty();
   }

   public void setOfflineQrCodeChallenge(Airlock2FAOfflineQrCodeChallenge offlineQrCodeChallenge) {
      this.offlineQrCodeChallenge = Optional.of(offlineQrCodeChallenge);
      this.onlineQrCodeChallenge = Optional.empty();
   }

   public void switchToOfflineMode(Airlock2FAPayloadEncryptionSettings payloadEncryptionSettings) {
      Preconditions.checkState(this.canEnableOfflineMode());
      this.remainingFactorsAndDevices.switchToOffline(payloadEncryptionSettings);
   }

   public void selectDevice(Airlock2FAAuthDevice device, Airlock2FAPayloadEncryptionSettings payloadEncryptionSettings) {
      this.remainingFactorsAndDevices.selectDevice(device, payloadEncryptionSettings);
   }

   public void selectBasedOnAuthTokenId(Airlock2FAAuthTokenId authTokenId, Airlock2FADeviceSettings settings) {
      this.remainingDevices().deviceWithId(authTokenId.deviceId()).ifPresent(device -> this.selectDevice(device, settings.payloadEncryptionSettings()));
      if (authTokenId.loginFactor() == Airlock2FAFactor.MOBILE_ONLY && settings.factorPriority().contains(Airlock2FAFactor.MOBILE_ONLY)) {
         this.setExclusiveFactor(Airlock2FAFactor.MOBILE_ONLY, settings.payloadEncryptionSettings());
      }
   }

   public Airlock2FAFactorPriority getFactors() {
      return this.remainingFactorsAndDevices.factors();
   }

   public Airlock2FAAuthDevice getOnlyDeviceOrThrow() {
      return this.remainingFactorsAndDevices.remainingDevices().onlyDeviceOrThrow();
   }

   public AuthenticationFactorDetail authenticationFactorDetail() {
      return this.bypasedFactor
         .or(() -> this.remainingFactorsAndDevices.topFactor())
         .map(Airlock2FAFactorInfoMapper::authenticationFactorDetailForAirlock2FAFactor)
         .orElse(KnownAuthenticationFactorDetail.PREAUTH_AIRLOCK_2FA);
   }

   public void setExclusiveFactor(Airlock2FAFactor factor, Airlock2FAPayloadEncryptionSettings payloadEncryptionSettings) {
      FailureException.checkTrue(this.remainingFactorsAndDevices.factors().contains(factor), "Factor must be contained in remaining factors.");
      this.remainingFactorsAndDevices.intersectFactors(List.of(factor), payloadEncryptionSettings);
   }

   public boolean canEnableOfflineMode() {
      return !this.isMobileOnlyApprovalOngoing() && this.remainingFactorsAndDevices.factors().containsOfflineFactor();
   }

   public boolean isOfflineModeEnabled() {
      return !this.remainingFactorsAndDevices.factors().containsOnlineFactor();
   }

   public boolean isMobileOnlyApprovalOngoing() {
      return this.topFactorEquals(Airlock2FAFactor.MOBILE_ONLY);
   }

   public void selectDeviceById(Airlock2FADeviceId deviceId, Airlock2FAPayloadEncryptionSettings payloadEncryptionSettings) {
      this.selectDevice(this.resolveDevice(deviceId), payloadEncryptionSettings);
   }

   private Airlock2FAAuthDevice resolveDevice(Airlock2FADeviceId id) {
      return this.allowedDevices.deviceWithId(id).orElseThrow(() -> new FailureException("No device with id."));
   }

   public Airlock2FAAuthLogData getLogData() {
      Optional<Airlock2FADeviceId> deviceId = Optional.of(this.remainingFactorsAndDevices.remainingDevices())
         .filter(Airlock2FADevices::hasOneDevice)
         .map(remaining -> remaining.onlyDeviceOrThrow().getId());
      Optional<Airlock2FASessionId> sessionId = this.onlineSession
         .<Airlock2FASessionId>map(online -> online.getSession().getSessionId())
         .or(() -> this.offlineSession.map(Airlock2FAFuturaeAuthSession::getSessionId));
      Optional<Airlock2FASessionId> fallbackSessionId = this.offlineSession
         .filter(offline -> this.onlineSession.isPresent())
         .map(Airlock2FAFuturaeAuthSession::getSessionId);
      return Airlock2FAAuthLogData.airlock2FAAuthLogData(this.getAccountIdOrThrow())
         .withDeviceIdIfPresent(deviceId)
         .withSessionIdIfPresent(sessionId)
         .withFallbackSessionIdIfPresent(fallbackSessionId);
   }

   public boolean isDeviceInCooldown(Airlock2FADeviceId deviceId) {
      return this.cooldownDevices().containsDeviceWithId(deviceId);
   }

   public boolean isDeviceAllowed(Airlock2FADeviceId deviceId) {
      return this.allowedDevices.containsDeviceWithId(deviceId);
   }

   @Override
   public MapifiedValues mapify() {
      return MapifiedValuesBuilder.mapifiedValues()
         .putIfPresent("accountId", this.accountId.map(Airlock2FAAccountId::getId))
         .putIfPresent("onlineSession", this.onlineSession.map(Airlock2FAOnlineApprovalSessionMapifier::mapifyAirlock2FAOnlineApprovalSession))
         .putIfPresent("offlineSession", this.offlineSession.map(Airlock2FAFuturaeAuthSessionMapifier::mapifyAirlock2FAFuturaeAuthSession))
         .putIfPresent("onlineQrCode", this.onlineQrCodeChallenge.map(Airlock2FAOnlineQrCodeChallenge::mapify))
         .putIfPresent("mobileOnly", this.mobileOnlyChallenge.map(Airlock2FAMobileOnlyChallenge::mapify))
         .putIfPresent("offlineQrCode", this.offlineQrCodeChallenge.map(Airlock2FAOfflineQrCodeChallenge::mapify))
         .putIfNotEmpty("sessionExchangeTokens", MapificationUtils.mapifyCollection(this.sessionExchangeTokens, Airlock2FAExchangeTokenData::mapify))
         .putIfPresent("sessionStartTime", this.sessionStartTime.map(MapificationUtils::mapifyInstant))
         .putIfNotEmpty("allowedDevices", this.allowedDevices.stream().map(Airlock2FAAuthDeviceMapifier::mapifyAirlock2FAAuthDevice).toList())
         .put("remainingFactorsAndDevices", Airlock2FARemainingFactorsAndDevicesMapifier.mapify(this.remainingFactorsAndDevices))
         .putIfPresent("bypassed", this.bypasedFactor.map(Enum::name))
         .putIfPresent("pollingCache", this.pollingCache.mapify())
         .putIfPresent("multiNumberedChallenge", this.multiNumberedChallenge.map(Airlock2FAMultiNumberedChallenge::value))
         .build();
   }

   public static Airlock2FAApprovalStepSession demapify(MapifiedValues values) {
      return new Airlock2FAApprovalStepSession(
         values.transformOptional("accountId", Airlock2FAAccountId::airlock2FAAccountId),
         values.demapifyOptional("onlineSession", Airlock2FAOnlineApprovalSessionMapifier::demapifyAirlock2FAOnlineApprovalSession),
         values.demapifyOptional("offlineSession", Airlock2FAFuturaeAuthSessionMapifier::demapifyAirlock2FAFuturaeAuthSession),
         values.demapifyOptional("onlineQrCode", Airlock2FAOnlineQrCodeChallenge::demapify),
         values.demapifyOptional("mobileOnly", Airlock2FAMobileOnlyChallenge::demapify),
         values.demapifyOptional("offlineQrCode", Airlock2FAOfflineQrCodeChallenge::demapify),
         values.demapifyList("sessionExchangeTokens", Airlock2FAExchangeTokenData::demapify),
         values.demapifyOptional("sessionStartTime", MapificationUtils::demapifyInstant),
         Airlock2FADevices.of(values.demapifyList("allowedDevices", Airlock2FAAuthDeviceMapifier::demapifyAirlock2FAAuthDevice)),
         values.demapifyValue("remainingFactorsAndDevices", Airlock2FARemainingFactorsAndDevicesMapifier::demapify),
         values.<String>getOptional("bypassed").map(Airlock2FAFactor::valueOf),
         PollingResultCache.demapifyPollingCache(values.getOptionalValues("pollingCache")),
         values.<Integer>getOptional("multiNumberedChallenge").map(Airlock2FAMultiNumberedChallenge::new)
      );
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAAccountId> getAccountId() {
      return this.accountId;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAOnlineApprovalSession> getOnlineSession() {
      return this.onlineSession;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAOnlineQrCodeChallenge> getOnlineQrCodeChallenge() {
      return this.onlineQrCodeChallenge;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAMobileOnlyChallenge> getMobileOnlyChallenge() {
      return this.mobileOnlyChallenge;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAOfflineQrCodeChallenge> getOfflineQrCodeChallenge() {
      return this.offlineQrCodeChallenge;
   }

   @SuppressFBWarnings(justification = "generated code")
   public List<Airlock2FAExchangeTokenData> getSessionExchangeTokens() {
      return this.sessionExchangeTokens;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Instant> getSessionStartTime() {
      return this.sessionStartTime;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public Airlock2FARemainingFactorsAndDevices getRemainingFactorsAndDevices() {
      return this.remainingFactorsAndDevices;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public Optional<Airlock2FAMultiNumberedChallenge> getMultiNumberedChallenge() {
      return this.multiNumberedChallenge;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Airlock2FAApprovalStepSession(PollingResultCache pollingCache) {
      this.pollingCache = pollingCache;
   }

   @SuppressFBWarnings(justification = "generated code")
   Airlock2FAApprovalStepSession(
      Optional<Airlock2FAAccountId> accountId,
      Optional<Airlock2FAOnlineApprovalSession> onlineSession,
      Optional<Airlock2FAFuturaeAuthSession> offlineSession,
      Optional<Airlock2FAOnlineQrCodeChallenge> onlineQrCodeChallenge,
      Optional<Airlock2FAMobileOnlyChallenge> mobileOnlyChallenge,
      Optional<Airlock2FAOfflineQrCodeChallenge> offlineQrCodeChallenge,
      List<Airlock2FAExchangeTokenData> sessionExchangeTokens,
      Optional<Instant> sessionStartTime,
      Airlock2FADevices allowedDevices,
      Airlock2FARemainingFactorsAndDevices remainingFactorsAndDevices,
      Optional<Airlock2FAFactor> bypasedFactor,
      PollingResultCache pollingCache,
      Optional<Airlock2FAMultiNumberedChallenge> multiNumberedChallenge
   ) {
      this.accountId = accountId;
      this.onlineSession = onlineSession;
      this.offlineSession = offlineSession;
      this.onlineQrCodeChallenge = onlineQrCodeChallenge;
      this.mobileOnlyChallenge = mobileOnlyChallenge;
      this.offlineQrCodeChallenge = offlineQrCodeChallenge;
      this.sessionExchangeTokens = sessionExchangeTokens;
      this.sessionStartTime = sessionStartTime;
      this.allowedDevices = allowedDevices;
      this.remainingFactorsAndDevices = remainingFactorsAndDevices;
      this.bypasedFactor = bypasedFactor;
      this.pollingCache = pollingCache;
      this.multiNumberedChallenge = multiNumberedChallenge;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof Airlock2FAApprovalStepSession other)) {
         return false;
      } else {
         Object this$accountId = this.accountId;
         Object other$accountId = other.accountId;
         if (this$accountId == null ? other$accountId == null : this$accountId.equals(other$accountId)) {
            Object this$onlineSession = this.onlineSession;
            Object other$onlineSession = other.onlineSession;
            if (this$onlineSession == null ? other$onlineSession == null : this$onlineSession.equals(other$onlineSession)) {
               Object this$offlineSession = this.offlineSession;
               Object other$offlineSession = other.offlineSession;
               if (this$offlineSession == null ? other$offlineSession == null : this$offlineSession.equals(other$offlineSession)) {
                  Object this$onlineQrCodeChallenge = this.onlineQrCodeChallenge;
                  Object other$onlineQrCodeChallenge = other.onlineQrCodeChallenge;
                  if (this$onlineQrCodeChallenge == null ? other$onlineQrCodeChallenge == null : this$onlineQrCodeChallenge.equals(other$onlineQrCodeChallenge)
                     )
                   {
                     Object this$mobileOnlyChallenge = this.mobileOnlyChallenge;
                     Object other$mobileOnlyChallenge = other.mobileOnlyChallenge;
                     if (this$mobileOnlyChallenge == null ? other$mobileOnlyChallenge == null : this$mobileOnlyChallenge.equals(other$mobileOnlyChallenge)) {
                        Object this$offlineQrCodeChallenge = this.offlineQrCodeChallenge;
                        Object other$offlineQrCodeChallenge = other.offlineQrCodeChallenge;
                        if (this$offlineQrCodeChallenge == null
                           ? other$offlineQrCodeChallenge == null
                           : this$offlineQrCodeChallenge.equals(other$offlineQrCodeChallenge)) {
                           Object this$sessionExchangeTokens = this.sessionExchangeTokens;
                           Object other$sessionExchangeTokens = other.sessionExchangeTokens;
                           if (this$sessionExchangeTokens == null
                              ? other$sessionExchangeTokens == null
                              : this$sessionExchangeTokens.equals(other$sessionExchangeTokens)) {
                              Object this$sessionStartTime = this.sessionStartTime;
                              Object other$sessionStartTime = other.sessionStartTime;
                              if (this$sessionStartTime == null ? other$sessionStartTime == null : this$sessionStartTime.equals(other$sessionStartTime)) {
                                 Object this$allowedDevices = this.allowedDevices;
                                 Object other$allowedDevices = other.allowedDevices;
                                 if (this$allowedDevices == null ? other$allowedDevices == null : this$allowedDevices.equals(other$allowedDevices)) {
                                    Object this$remainingFactorsAndDevices = this.remainingFactorsAndDevices;
                                    Object other$remainingFactorsAndDevices = other.remainingFactorsAndDevices;
                                    if (this$remainingFactorsAndDevices == null
                                       ? other$remainingFactorsAndDevices == null
                                       : this$remainingFactorsAndDevices.equals(other$remainingFactorsAndDevices)) {
                                       Object this$bypasedFactor = this.bypasedFactor;
                                       Object other$bypasedFactor = other.bypasedFactor;
                                       if (this$bypasedFactor == null ? other$bypasedFactor == null : this$bypasedFactor.equals(other$bypasedFactor)) {
                                          Object this$pollingCache = this.pollingCache;
                                          Object other$pollingCache = other.pollingCache;
                                          if (this$pollingCache == null ? other$pollingCache == null : this$pollingCache.equals(other$pollingCache)) {
                                             Object this$multiNumberedChallenge = this.multiNumberedChallenge;
                                             Object other$multiNumberedChallenge = other.multiNumberedChallenge;
                                             return this$multiNumberedChallenge == null
                                                ? other$multiNumberedChallenge == null
                                                : this$multiNumberedChallenge.equals(other$multiNumberedChallenge);
                                          } else {
                                             return false;
                                          }
                                       } else {
                                          return false;
                                       }
                                    } else {
                                       return false;
                                    }
                                 } else {
                                    return false;
                                 }
                              } else {
                                 return false;
                              }
                           } else {
                              return false;
                           }
                        } else {
                           return false;
                        }
                     } else {
                        return false;
                     }
                  } else {
                     return false;
                  }
               } else {
                  return false;
               }
            } else {
               return false;
            }
         } else {
            return false;
         }
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public int hashCode() {
      int PRIME = 59;
      int result = 1;
      Object $accountId = this.accountId;
      result = result * 59 + ($accountId == null ? 43 : $accountId.hashCode());
      Object $onlineSession = this.onlineSession;
      result = result * 59 + ($onlineSession == null ? 43 : $onlineSession.hashCode());
      Object $offlineSession = this.offlineSession;
      result = result * 59 + ($offlineSession == null ? 43 : $offlineSession.hashCode());
      Object $onlineQrCodeChallenge = this.onlineQrCodeChallenge;
      result = result * 59 + ($onlineQrCodeChallenge == null ? 43 : $onlineQrCodeChallenge.hashCode());
      Object $mobileOnlyChallenge = this.mobileOnlyChallenge;
      result = result * 59 + ($mobileOnlyChallenge == null ? 43 : $mobileOnlyChallenge.hashCode());
      Object $offlineQrCodeChallenge = this.offlineQrCodeChallenge;
      result = result * 59 + ($offlineQrCodeChallenge == null ? 43 : $offlineQrCodeChallenge.hashCode());
      Object $sessionExchangeTokens = this.sessionExchangeTokens;
      result = result * 59 + ($sessionExchangeTokens == null ? 43 : $sessionExchangeTokens.hashCode());
      Object $sessionStartTime = this.sessionStartTime;
      result = result * 59 + ($sessionStartTime == null ? 43 : $sessionStartTime.hashCode());
      Object $allowedDevices = this.allowedDevices;
      result = result * 59 + ($allowedDevices == null ? 43 : $allowedDevices.hashCode());
      Object $remainingFactorsAndDevices = this.remainingFactorsAndDevices;
      result = result * 59 + ($remainingFactorsAndDevices == null ? 43 : $remainingFactorsAndDevices.hashCode());
      Object $bypasedFactor = this.bypasedFactor;
      result = result * 59 + ($bypasedFactor == null ? 43 : $bypasedFactor.hashCode());
      Object $pollingCache = this.pollingCache;
      result = result * 59 + ($pollingCache == null ? 43 : $pollingCache.hashCode());
      Object $multiNumberedChallenge = this.multiNumberedChallenge;
      return result * 59 + ($multiNumberedChallenge == null ? 43 : $multiNumberedChallenge.hashCode());
   }

   @SuppressFBWarnings(justification = "generated code")
   public PollingResultCache getPollingCache() {
      return this.pollingCache;
   }

   @SuppressFBWarnings(justification = "generated code")
   public void setMultiNumberedChallenge(Optional<Airlock2FAMultiNumberedChallenge> multiNumberedChallenge) {
      this.multiNumberedChallenge = multiNumberedChallenge;
   }
}
