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

import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.common.domain.service.mapification.Mapifier;
import com.airlock.iam.core.misc.impl.authen.cronto.CrontoPushDeviceChoice;
import com.airlock.iam.core.misc.impl.cronto.CrontoChallengeResult;
import com.airlock.iam.flow.api.domain.model.step.StepSession;
import com.airlock.iam.flow.shared.domain.model.session.PollingResultCache;
import com.airlock.iam.flow.shared.domain.service.cronto.CrontoCheckingServiceImpl;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import lombok.NonNull;

public final class CrontoCheckingStepSession implements StepSession {
   private static final String SESSION_KEY = "session";
   private static final String CHALLENGE_KEY = "challenge";
   private static final String CHOICES_KEY = "choices";
   private final PollingResultCache pollingCache;
   private final CrontoCheckingServiceImpl.CrontoCheckingSession serviceSession;
   private Optional<CrontoAuthChallenge> challenge = Optional.empty();
   private List<CrontoPushDeviceChoice> pushDeviceChoices = Collections.emptyList();

   public void setChallenge(@NonNull CrontoAuthChallenge challenge) {
      if (challenge == null) {
         throw new NullPointerException("challenge is marked non-null but is null");
      }

      this.challenge = Optional.of(challenge);
      this.pushDeviceChoices = Collections.emptyList();
   }

   public void clearChallenge() {
      this.challenge = Optional.empty();
   }

   public boolean isExpectingOtpVerification() {
      return this.challenge.isPresent();
   }

   public boolean isOnlineVerificationPossible() {
      return this.challenge.isPresent() && this.challenge.get().isOnlineValidation();
   }

   public void updateChallengeMethod(CrontoChallengeResult.CrontoMethod usedMethod) {
      this.challenge = Optional.of(this.challenge.orElseThrow().withUpdatedMethod(usedMethod));
   }

   @Override
   public MapifiedValues mapify() {
      Mapifier mapifier = Mapifier.getInstance();
      return MapifiedValuesBuilder.mapifiedValues()
         .put("session", mapifier.mapify(this.serviceSession))
         .putIfPresent("challenge", this.challenge.map(mapifier::mapify))
         .putIfNotEmpty("choices", this.pushDeviceChoices.stream().map(CrontoPushDeviceChoice::mapify).toList())
         .putIfPresent("pollingCache", this.pollingCache.mapify())
         .build();
   }

   public static CrontoCheckingStepSession demapify(MapifiedValues values) {
      Mapifier mapifier = Mapifier.getInstance();
      return new CrontoCheckingStepSession(
         PollingResultCache.demapifyPollingCache(values.getOptionalValues("pollingCache")),
         mapifier.demapify(values.getValues("session")),
         values.demapifyOptional("challenge", mapifier::demapify),
         values.demapifyList("choices", CrontoPushDeviceChoice::demapify)
      );
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public CrontoCheckingServiceImpl.CrontoCheckingSession getServiceSession() {
      return this.serviceSession;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<CrontoAuthChallenge> getChallenge() {
      return this.challenge;
   }

   @SuppressFBWarnings(justification = "generated code")
   public List<CrontoPushDeviceChoice> getPushDeviceChoices() {
      return this.pushDeviceChoices;
   }

   @SuppressFBWarnings(justification = "generated code")
   public CrontoCheckingStepSession(PollingResultCache pollingCache, CrontoCheckingServiceImpl.CrontoCheckingSession serviceSession) {
      this.pollingCache = pollingCache;
      this.serviceSession = serviceSession;
   }

   @SuppressFBWarnings(justification = "generated code")
   private CrontoCheckingStepSession(
      PollingResultCache pollingCache,
      CrontoCheckingServiceImpl.CrontoCheckingSession serviceSession,
      Optional<CrontoAuthChallenge> challenge,
      List<CrontoPushDeviceChoice> pushDeviceChoices
   ) {
      this.pollingCache = pollingCache;
      this.serviceSession = serviceSession;
      this.challenge = challenge;
      this.pushDeviceChoices = pushDeviceChoices;
   }

   @SuppressFBWarnings(justification = "generated code")
   public void setPushDeviceChoices(List<CrontoPushDeviceChoice> pushDeviceChoices) {
      this.pushDeviceChoices = pushDeviceChoices;
   }
}
