package com.airlock.iam.authentication.domain.model.targetapp;

import com.airlock.iam.authentication.application.configuration.AuthenticationFlowConfigSpec;
import com.airlock.iam.authentication.application.configuration.AuthorizationFlowConfig;
import com.airlock.iam.authentication.application.configuration.targetapp.TargetApplicationConfigSpec;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.flow.api.domain.model.flow.ApplicationId;
import com.airlock.iam.flow.application.configuration.FlowConfig;
import com.airlock.iam.flow.application.configuration.flow.FlowConfigHash;
import com.airlock.iam.login.api.domain.model.location.ForwardLocation;
import com.airlock.iam.login.api.domain.model.targetapp.URIBasedApplicationSelector;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import lombok.NonNull;

@LogicForConfig(TargetApplicationConfigSpec.class)
public class TargetApplication {
   private final ApplicationId applicationId;
   private final Optional<URIBasedApplicationSelector> uriSelector;
   private final AuthenticationFlowConfigSpec authenticationFlow;
   private final Optional<AuthorizationFlowConfig> authorizationFlow;
   private final TargetApplication.TargetApplicationType type;

   @Inject
   public TargetApplication(
      @NonNull ApplicationId applicationId,
      @NonNull Optional<URIBasedApplicationSelector> uriSelector,
      @NonNull AuthenticationFlowConfigSpec authenticationFlow,
      @NonNull Optional<AuthorizationFlowConfig> authorizationFlow,
      @NonNull TargetApplication.TargetApplicationType type
   ) {
      if (applicationId == null) {
         throw new NullPointerException("applicationId is marked non-null but is null");
      }

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

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

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

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

      this.applicationId = applicationId;
      this.uriSelector = uriSelector;
      this.authenticationFlow = authenticationFlow;
      this.authorizationFlow = authorizationFlow;
      this.type = type;
   }

   public boolean isSelected(ApplicationId appId) {
      return this.applicationId.equals(appId);
   }

   public boolean isSelected(ForwardLocation location) {
      return this.uriSelector.<Boolean>map(selector -> selector.isSelected(location)).orElse(false);
   }

   public List<FlowConfigHash> getAllFlowConfigHashes() {
      List<FlowConfigHash> hashes = new ArrayList<>();
      hashes.add(this.authenticationFlow.getFlowConfigHash());
      this.authorizationFlow.map(FlowConfig::getFlowConfigHash).ifPresent(hashes::add);
      return hashes;
   }

   public Optional<FlowConfig> getFlowByConfigHash(FlowConfigHash configHash) {
      if (this.authenticationFlow.getFlowConfigHash().equals(configHash)) {
         return Optional.of(this.authenticationFlow);
      } else {
         return this.authorizationFlow.isPresent() && this.authorizationFlow.get().getFlowConfigHash().equals(configHash)
            ? Optional.of(this.authorizationFlow.get())
            : Optional.empty();
      }
   }

   public TargetApplication.TargetApplicationType getApplicationType() {
      return this.type;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public ApplicationId getApplicationId() {
      return this.applicationId;
   }

   @SuppressFBWarnings(justification = "generated code")
   public AuthenticationFlowConfigSpec getAuthenticationFlow() {
      return this.authenticationFlow;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class TargetApplicationBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private ApplicationId applicationId;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<URIBasedApplicationSelector> uriSelector;
      @SuppressFBWarnings(justification = "generated code")
      private AuthenticationFlowConfigSpec authenticationFlow;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<AuthorizationFlowConfig> authorizationFlow;
      @SuppressFBWarnings(justification = "generated code")
      private TargetApplication.TargetApplicationType type;

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

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplication.TargetApplicationBuilder applicationId(ApplicationId applicationId) {
         this.applicationId = applicationId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplication.TargetApplicationBuilder uriSelector(Optional<URIBasedApplicationSelector> uriSelector) {
         this.uriSelector = uriSelector;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplication.TargetApplicationBuilder authenticationFlow(AuthenticationFlowConfigSpec authenticationFlow) {
         this.authenticationFlow = authenticationFlow;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplication.TargetApplicationBuilder authorizationFlow(Optional<AuthorizationFlowConfig> authorizationFlow) {
         this.authorizationFlow = authorizationFlow;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplication.TargetApplicationBuilder type(TargetApplication.TargetApplicationType type) {
         this.type = type;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplication build() {
         return new TargetApplication(this.applicationId, this.uriSelector, this.authenticationFlow, this.authorizationFlow, this.type);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "TargetApplication.TargetApplicationBuilder(applicationId="
            + this.applicationId
            + ", uriSelector="
            + this.uriSelector
            + ", authenticationFlow="
            + this.authenticationFlow
            + ", authorizationFlow="
            + this.authorizationFlow
            + ", type="
            + this.type
            + ")";
      }
   }

   public enum TargetApplicationType {
      STANDARD,
      ONE_SHOT;
   }
}
