package com.airlock.iam.flow.domain.model.step;

import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.application.configuration.step.FlowStepConfig;
import com.airlock.iam.flow.api.application.configuration.step.RedFlagHandlingStepConfig;
import com.airlock.iam.flow.api.domain.model.step.FlowCondition;
import com.airlock.iam.flow.api.domain.model.step.StepId;
import com.airlock.iam.flow.domain.model.selection.condition.StepActivationFlowCondition;
import com.airlock.iam.flow.domain.model.session.InternalFlowSession;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Provider;
import java.util.Optional;
import lombok.NonNull;

public final class FlowStepConditionChecker {
   private static final ELogger LOG = ELoggerFactory.getLogger(FlowStepConditionChecker.class);
   @NonNull
   private final FlowStepConfig config;
   @NonNull
   private final Provider<InternalFlowSession> flowSession;
   @NonNull
   private final Optional<FlowCondition> skipCondition;
   @NonNull
   private final Optional<FlowCondition> preCondition;
   @NonNull
   private final Optional<StepActivationFlowCondition> activationCondition;

   public boolean preconditionSatisfied() {
      return this.preCondition.map(FlowCondition::isFulfilled).orElse(true);
   }

   public boolean skipThisStep() {
      if (this.config instanceof RedFlagHandlingStepConfig) {
         return !this.sessionContainsMatchingRedFlag((RedFlagHandlingStepConfig)this.config);
      }

      if (this.activationCondition.isPresent()) {
         StepId stepId = this.config.getStepId().orElseThrow();
         if (!this.activationCondition.get().isFulfilled()) {
            LOG.info("Skipping step " + stepId + " because activation condition is not fulfilled.");
            return true;
         }

         LOG.debug("Activation condition for " + stepId + " is fulfilled. Checking skip condition...");
      }

      boolean skip = this.skipCondition.map(FlowCondition::isFulfilled).orElse(false);
      if (skip) {
         LOG.info("Skipping step because skip condition is fulfilled: " + this.skipCondition.get().displayName());
      }

      return skip;
   }

   private boolean sessionContainsMatchingRedFlag(RedFlagHandlingStepConfig redFlagHandlerStepConfig) {
      return ((InternalFlowSession)this.flowSession.get()).getRedFlags().contains(redFlagHandlerStepConfig.getRedFlag().toRedFlag());
   }

   @SuppressFBWarnings(justification = "generated code")
   public static FlowStepConditionChecker.FlowStepConditionCheckerBuilder builder() {
      return new FlowStepConditionChecker.FlowStepConditionCheckerBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public FlowStepConditionChecker(
      @NonNull FlowStepConfig config,
      @NonNull Provider<InternalFlowSession> flowSession,
      @NonNull Optional<FlowCondition> skipCondition,
      @NonNull Optional<FlowCondition> preCondition,
      @NonNull Optional<StepActivationFlowCondition> activationCondition
   ) {
      if (config == null) {
         throw new NullPointerException("config is marked non-null but is null");
      }

      if (flowSession == null) {
         throw new NullPointerException("flowSession is marked non-null but is null");
      }

      if (skipCondition == null) {
         throw new NullPointerException("skipCondition is marked non-null but is null");
      }

      if (preCondition == null) {
         throw new NullPointerException("preCondition is marked non-null but is null");
      }

      if (activationCondition == null) {
         throw new NullPointerException("activationCondition is marked non-null but is null");
      }

      this.config = config;
      this.flowSession = flowSession;
      this.skipCondition = skipCondition;
      this.preCondition = preCondition;
      this.activationCondition = activationCondition;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class FlowStepConditionCheckerBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private FlowStepConfig config;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<InternalFlowSession> flowSession;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<FlowCondition> skipCondition;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<FlowCondition> preCondition;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<StepActivationFlowCondition> activationCondition;

      @SuppressFBWarnings(justification = "generated code")
      FlowStepConditionCheckerBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowStepConditionChecker.FlowStepConditionCheckerBuilder config(@NonNull FlowStepConfig config) {
         if (config == null) {
            throw new NullPointerException("config is marked non-null but is null");
         }

         this.config = config;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowStepConditionChecker.FlowStepConditionCheckerBuilder flowSession(@NonNull Provider<InternalFlowSession> flowSession) {
         if (flowSession == null) {
            throw new NullPointerException("flowSession is marked non-null but is null");
         }

         this.flowSession = flowSession;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowStepConditionChecker.FlowStepConditionCheckerBuilder skipCondition(@NonNull Optional<FlowCondition> skipCondition) {
         if (skipCondition == null) {
            throw new NullPointerException("skipCondition is marked non-null but is null");
         }

         this.skipCondition = skipCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowStepConditionChecker.FlowStepConditionCheckerBuilder preCondition(@NonNull Optional<FlowCondition> preCondition) {
         if (preCondition == null) {
            throw new NullPointerException("preCondition is marked non-null but is null");
         }

         this.preCondition = preCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowStepConditionChecker.FlowStepConditionCheckerBuilder activationCondition(@NonNull Optional<StepActivationFlowCondition> activationCondition) {
         if (activationCondition == null) {
            throw new NullPointerException("activationCondition is marked non-null but is null");
         }

         this.activationCondition = activationCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FlowStepConditionChecker build() {
         return new FlowStepConditionChecker(this.config, this.flowSession, this.skipCondition, this.preCondition, this.activationCondition);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "FlowStepConditionChecker.FlowStepConditionCheckerBuilder(config="
            + this.config
            + ", flowSession="
            + this.flowSession
            + ", skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", activationCondition="
            + this.activationCondition
            + ")";
      }
   }
}
