package com.airlock.iam.publicselfservice.domain.service.restrictions;

import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.common.domain.model.principal.UserPrincipal;
import com.airlock.iam.core.misc.persistency.PersistentUserWithPassword;
import com.airlock.iam.flow.api.domain.model.session.FlowAttributeRepository;
import com.airlock.iam.flow.api.domain.model.session.SingleFlowScopedFlowAttribute;
import com.airlock.iam.publicselfservice.api.domain.model.PublicSelfServiceRestriction;
import com.airlock.iam.publicselfservice.application.configuration.flow.PublicSelfServiceSettings;
import com.airlock.iam.publicselfservice.application.configuration.restrictions.TooManyUnlocksRestrictionConfig;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;

@LogicForConfig(TooManyUnlocksRestrictionConfig.class)
public final class TooManyUnlocksRestriction implements PublicSelfServiceRestriction {
   private final Provider<PublicSelfServiceSettings> settings;
   private final Provider<TooManyUnlocksRestrictionConfig> config;
   private final Provider<UserPrincipal> userPrincipal;
   private final Provider<FlowAttributeRepository> attributes;

   @Inject
   public TooManyUnlocksRestriction(
      Provider<PublicSelfServiceSettings> settings,
      Provider<TooManyUnlocksRestrictionConfig> config,
      Provider<UserPrincipal> userPrincipal,
      Provider<FlowAttributeRepository> attributes
   ) {
      this.settings = settings;
      this.config = config;
      this.userPrincipal = userPrincipal;
      this.attributes = attributes;
   }

   @Override
   public Optional<PublicSelfServiceRestriction.RestrictionViolation> getViolation() {
      if (((UserPrincipal)this.userPrincipal.get()).exists() && !((PublicSelfServiceSettings)this.settings.get()).getMaxNumberUnlocks().isEmpty()) {
         PersistentUserWithPassword user = (PersistentUserWithPassword)((UserPrincipal)this.userPrincipal.get()).user();
         int unlocksAtFlowStart = user.getUnlockAttempts() - this.unlocksInCurrentFlow();
         return unlocksAtFlowStart >= ((PublicSelfServiceSettings)this.settings.get()).getMaxNumberUnlocks().get()
            ? Optional.of(
               new PublicSelfServiceRestriction.RestrictionViolation(
                  TooManyUnlocksRestriction.TooManyUnlocksRestrictionCode.TOO_MANY_UNLOCKS,
                  ((TooManyUnlocksRestrictionConfig)this.config.get()).isEnableFeedback()
               )
            )
            : Optional.empty();
      } else {
         return Optional.empty();
      }
   }

   private int unlocksInCurrentFlow() {
      return ((FlowAttributeRepository)this.attributes.get())
         .tryGetAttribute(TooManyUnlocksRestriction.UnlocksInCurrentFlow.class)
         .map(TooManyUnlocksRestriction.UnlocksInCurrentFlow::getNumUnlocks)
         .orElse(0);
   }

   public enum TooManyUnlocksRestrictionCode implements PublicSelfServiceRestriction.ViolationCode {
      TOO_MANY_UNLOCKS;
   }

   public static final class UnlocksInCurrentFlow implements SingleFlowScopedFlowAttribute<TooManyUnlocksRestriction.UnlocksInCurrentFlow> {
      public static final String NUM_UNLOCKS_KEY = "numUnlocks";
      private final int numUnlocks;

      public TooManyUnlocksRestriction.UnlocksInCurrentFlow mergeWith(TooManyUnlocksRestriction.UnlocksInCurrentFlow newAttribute) {
         return new TooManyUnlocksRestriction.UnlocksInCurrentFlow(this.numUnlocks + newAttribute.numUnlocks);
      }

      @Override
      public MapifiedValues mapify() {
         return MapifiedValuesBuilder.mapifiedValues().put("numUnlocks", this.numUnlocks).build();
      }

      public static TooManyUnlocksRestriction.UnlocksInCurrentFlow demapify(MapifiedValues values) {
         return new TooManyUnlocksRestriction.UnlocksInCurrentFlow(values.<Integer>get("numUnlocks"));
      }

      @SuppressFBWarnings(justification = "generated code")
      public UnlocksInCurrentFlow(int numUnlocks) {
         this.numUnlocks = numUnlocks;
      }

      @SuppressFBWarnings(justification = "generated code")
      public int getNumUnlocks() {
         return this.numUnlocks;
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public boolean equals(Object o) {
         if (o == this) {
            return true;
         } else {
            return !(o instanceof TooManyUnlocksRestriction.UnlocksInCurrentFlow other) ? false : this.numUnlocks == other.numUnlocks;
         }
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public int hashCode() {
         int PRIME = 59;
         int result = 1;
         return result * 59 + this.numUnlocks;
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "TooManyUnlocksRestriction.UnlocksInCurrentFlow(numUnlocks=" + this.getNumUnlocks() + ")";
      }
   }
}
