package com.airlock.iam.authentication.application.configuration.targetapp;

import com.airlock.iam.authentication.application.configuration.AuthenticationFlowConfig;
import com.airlock.iam.authentication.application.configuration.AuthorizationFlowConfig;
import com.airlock.iam.authentication.application.configuration.idpropagation.IdentityPropagationConfig;
import com.airlock.iam.common.api.application.configuration.valueprovider.UsernameToPropagateProviderConfig;
import com.airlock.iam.common.application.configuration.valueprovider.UserPrincipalNameProviderConfig;
import com.airlock.iam.flow.api.domain.model.flow.ApplicationId;
import com.airlock.iam.flow.application.configuration.validation.FlowStepIdConfigValidators;
import com.airlock.iam.login.api.application.configuration.targetapp.URIBasedApplicationSelectorConfig;
import com.airlock.iam.login.application.configuration.targetapp.ApplicationIdConfig;
import com.airlock.iam.login.application.configuration.targetapp.WafCredentialProviderConfig;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.PluginListProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.Environment;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.ConfigurationException;
import com.airlock.iam.plugin.api.valuereader.PluginPropertyValueReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

@Plugin(description = "Specification of authentication, authorization and identity propagation for an application to be protected by Airlock IAM.")
public class TargetApplicationConfig implements TargetApplicationConfigSpec {
   private static final String APPLICATION_SELECTOR_DISPLAY_NAME = "Application Selector";
   @PluginProperty(
      description = "Unique ID of this application. The ID must be used in the authorization endpoint to indicate that access to this application is requested.",
      displayName = "Application ID",
      optional = false
   )
   private ApplicationIdConfig applicationId;
   @PluginProperty(
      description = "Instead of the application ID, an URI might be used in the authorization endpoint to indicate that access to this application is requested.<p>The first application that matches the forward URI is used.<br/></p><p>The selector is <b>ignored for the default application</b>, which is always used when none of the other applications match.</p><p>In combination with Authorization Conditions in Protected Self-Service Flows, the Application Selector can be used to trigger step-up authentication.</p>",
      displayName = "Application Selector",
      optional = true
   )
   private URIBasedApplicationSelectorConfig applicationSelector;
   @PluginProperty(
      description = "The authentication flow determines the user identity and awards tags to the user session for completed authentication steps. After successful completion of the authentication flow, the user is considered identified and the tags are awarded, but identity propagation will not be performed until the authorization flow is also completed successfully.",
      optional = false,
      displayName = "Authentication Flow"
   )
   private AuthenticationFlowConfig authenticationFlow;
   public static final String AUTHENTICATION_FLOW_DISPLAY_NAME = "Authentication Flow";
   @PluginProperty(
      description = "The authorization flow that comes after successful completion of the authentication flow. Identity propagation and awarding of Airlock Gateway roles are only done once the authorization is successfully completed.",
      displayName = "Authorization Flow",
      optional = true
   )
   private AuthorizationFlowConfig authorizationFlow;
   @PluginListProperty(
      description = "<p>These are the Airlock Gateway roles (credentials) that are set after the authentication flow and the corresponding authorization flow (if configured) have successfully been completed. These roles are used on the Gateway to control the access to protected backends. If backend applications need user roles, those must be configured in the identity propagators.</p><p>Depending on the Gateway Settings, these roles either replace the current Gateway roles or are added to them (default).</p>",
      optional = true
   )
   private List<WafCredentialProviderConfig> airlockGatewayRoles;
   @PluginListProperty(
      displayName = "Identity Propagation",
      description = "Identity propagators add cookies and/or headers to the response after successful authorization. The identity propagators are applied in the order in which they are configured.",
      optional = true
   )
   private List<IdentityPropagationConfig> identityPropagation;
   public static final String IDENTITY_PROPAGATION_DISPLAY_NAME = "Identity Propagation";
   @PluginProperty(
      description = "Provides a value to be propagated to the backend applications by the identity propagators.",
      optional = true,
      defaultValue = UserPrincipalNameProviderConfig.class
   )
   private UsernameToPropagateProviderConfig usernameToPropagateProvider;

   public ApplicationIdConfig getId() {
      return this.applicationId;
   }

   @Override
   public ApplicationId getApplicationId() {
      return this.applicationId.getId();
   }

   @Override
   public Optional<AuthorizationFlowConfig> getAuthorizationFlow() {
      return Optional.ofNullable(this.authorizationFlow);
   }

   @Override
   public URIBasedApplicationSelectorConfig getApplicationSelector() {
      return this.applicationSelector;
   }

   public AuthenticationFlowConfig getAuthenticationFlow() {
      return this.authenticationFlow;
   }

   @Override
   public List<WafCredentialProviderConfig> getAirlockGatewayRoles() {
      return this.airlockGatewayRoles;
   }

   @Override
   public List<IdentityPropagationConfig> getIdentityPropagation() {
      return this.identityPropagation;
   }

   @Override
   public UsernameToPropagateProviderConfig getUsernameToPropagateProvider() {
      return this.usernameToPropagateProvider;
   }

   @ConfigurationValidator
   static void validateFlowConstraints(
      PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment
   ) throws ConfigurationException {
      new FlowStepIdConfigValidators(
            config,
            env,
            context,
            false,
            PluginPropertyValueReader.effectivePluginPropertyValue(config, "authenticationFlow", env, context),
            PluginPropertyValueReader.effectivePluginPropertyValue(config, "authorizationFlow", env, context)
         )
         .validateFlowConstraints();
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public TargetApplicationConfig(
      ApplicationIdConfig applicationId,
      URIBasedApplicationSelectorConfig applicationSelector,
      AuthenticationFlowConfig authenticationFlow,
      AuthorizationFlowConfig authorizationFlow,
      List<WafCredentialProviderConfig> airlockGatewayRoles,
      List<IdentityPropagationConfig> identityPropagation,
      UsernameToPropagateProviderConfig usernameToPropagateProvider
   ) {
      this.applicationId = applicationId;
      this.applicationSelector = applicationSelector;
      this.authenticationFlow = authenticationFlow;
      this.authorizationFlow = authorizationFlow;
      this.airlockGatewayRoles = airlockGatewayRoles;
      this.identityPropagation = identityPropagation;
      this.usernameToPropagateProvider = usernameToPropagateProvider;
   }

   @SuppressFBWarnings(justification = "generated code")
   public TargetApplicationConfig() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class TargetApplicationConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private ApplicationIdConfig applicationId;
      @SuppressFBWarnings(justification = "generated code")
      private URIBasedApplicationSelectorConfig applicationSelector;
      @SuppressFBWarnings(justification = "generated code")
      private AuthenticationFlowConfig authenticationFlow;
      @SuppressFBWarnings(justification = "generated code")
      private AuthorizationFlowConfig authorizationFlow;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<WafCredentialProviderConfig> airlockGatewayRoles;
      @SuppressFBWarnings(justification = "generated code")
      private List<IdentityPropagationConfig> identityPropagation;
      @SuppressFBWarnings(justification = "generated code")
      private UsernameToPropagateProviderConfig usernameToPropagateProvider;

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

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder applicationId(ApplicationIdConfig applicationId) {
         this.applicationId = applicationId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder applicationSelector(URIBasedApplicationSelectorConfig applicationSelector) {
         this.applicationSelector = applicationSelector;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder authenticationFlow(AuthenticationFlowConfig authenticationFlow) {
         this.authenticationFlow = authenticationFlow;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder authorizationFlow(AuthorizationFlowConfig authorizationFlow) {
         this.authorizationFlow = authorizationFlow;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder airlockGatewayRole(WafCredentialProviderConfig airlockGatewayRole) {
         if (this.airlockGatewayRoles == null) {
            this.airlockGatewayRoles = new ArrayList<>();
         }

         this.airlockGatewayRoles.add(airlockGatewayRole);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder airlockGatewayRoles(Collection<? extends WafCredentialProviderConfig> airlockGatewayRoles) {
         if (airlockGatewayRoles == null) {
            throw new NullPointerException("airlockGatewayRoles cannot be null");
         }

         if (this.airlockGatewayRoles == null) {
            this.airlockGatewayRoles = new ArrayList<>();
         }

         this.airlockGatewayRoles.addAll(airlockGatewayRoles);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder clearAirlockGatewayRoles() {
         if (this.airlockGatewayRoles != null) {
            this.airlockGatewayRoles.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder identityPropagation(List<IdentityPropagationConfig> identityPropagation) {
         this.identityPropagation = identityPropagation;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig.TargetApplicationConfigBuilder usernameToPropagateProvider(UsernameToPropagateProviderConfig usernameToPropagateProvider) {
         this.usernameToPropagateProvider = usernameToPropagateProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TargetApplicationConfig build() {
         return new TargetApplicationConfig(
            this.applicationId,
            this.applicationSelector,
            this.authenticationFlow,
            this.authorizationFlow,
            switch (this.airlockGatewayRoles == null ? 0 : this.airlockGatewayRoles.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.airlockGatewayRoles.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.airlockGatewayRoles));
            },
            this.identityPropagation,
            this.usernameToPropagateProvider
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "TargetApplicationConfig.TargetApplicationConfigBuilder(applicationId="
            + this.applicationId
            + ", applicationSelector="
            + this.applicationSelector
            + ", authenticationFlow="
            + this.authenticationFlow
            + ", authorizationFlow="
            + this.authorizationFlow
            + ", airlockGatewayRoles="
            + this.airlockGatewayRoles
            + ", identityPropagation="
            + this.identityPropagation
            + ", usernameToPropagateProvider="
            + this.usernameToPropagateProvider
            + ")";
      }
   }
}
