package com.airlock.iam.oauth2.application.configuration.resource;

import com.airlock.iam.core.misc.util.ticket.SecureTicketEncoder;
import com.airlock.iam.oauth2.api.application.configuration.claims.ClaimConditionConfig;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringListProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Plugin(
   description = "<p>OAuth 2.0 resource provider returning an SSO Ticket to be used for authentication.</p><p><b>Security Warning:</b> This resource allows exchanging an Access Token for an SSO Ticket which may provide much more access than intended. This implies that <b>everyone</b> in possession of an access token can impersonate the user.</p><p>The ticket only contains the username and the static roles.</p><p>This plugin is intended to be used as <code>login_hint</code> parameter in OpenID Connect flows and requires a configured \"OpenID Connect SSO Ticket Login Hint\" on the authorization server.</p>",
   displayName = "OAuth 2.0 SSO Ticket Resource"
)
@PluginLicenseRestriction(moduleAffiliation = "OAuthServer")
public class OAuth2SsoTicketResourceProviderConfig extends AbstractOAuth2SingleResourceProviderConfig {
   public static final String OAUTH_2_0_SSO_TICKET_RESOURCE_DISPLAY_NAME = "OAuth 2.0 SSO Ticket Resource";
   @IntegerProperty(
      description = "<p>The SSO ticket lifetime in seconds.</p><p>This should be configured as short as possible.</p>",
      displayName = "Ticket Lifetime [s]",
      exampleValues = {5, 20},
      optional = true,
      defaultValue = 10,
      minValue = 1
   )
   private int ticketLifetimeInSeconds;
   @PluginProperty(description = "The ticket encoder plugin used to sign and encrypt the SSO ticket.", optional = false)
   private SecureTicketEncoder encoder;
   @StringListProperty(
      description = "Static list of roles granted to the user in the ticket. Can be used to assign special roles to the user, for example for Step-Up scenarios.",
      optional = true,
      exampleElements = {"sso", "oauth2"}
   )
   private List<String> staticRoles;

   public OAuth2SsoTicketResourceProviderConfig(
      String identifier, int ticketLifetimeInSeconds, SecureTicketEncoder encoder, List<String> staticRoles, ClaimConditionConfig condition
   ) {
      super(identifier, condition);
      this.ticketLifetimeInSeconds = ticketLifetimeInSeconds;
      this.encoder = encoder;
      this.staticRoles = staticRoles;
   }

   public int getTicketLifetimeInSeconds() {
      return this.ticketLifetimeInSeconds;
   }

   public SecureTicketEncoder getEncoder() {
      return this.encoder;
   }

   public List<String> getStaticRoles() {
      return this.staticRoles;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2SsoTicketResourceProviderConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String identifier;
      @SuppressFBWarnings(justification = "generated code")
      private int ticketLifetimeInSeconds;
      @SuppressFBWarnings(justification = "generated code")
      private SecureTicketEncoder encoder;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<String> staticRoles;
      @SuppressFBWarnings(justification = "generated code")
      private ClaimConditionConfig condition;

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

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2SsoTicketResourceProviderConfig.OAuth2SsoTicketResourceProviderConfigBuilder identifier(String identifier) {
         this.identifier = identifier;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2SsoTicketResourceProviderConfig.OAuth2SsoTicketResourceProviderConfigBuilder ticketLifetimeInSeconds(int ticketLifetimeInSeconds) {
         this.ticketLifetimeInSeconds = ticketLifetimeInSeconds;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2SsoTicketResourceProviderConfig.OAuth2SsoTicketResourceProviderConfigBuilder encoder(SecureTicketEncoder encoder) {
         this.encoder = encoder;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2SsoTicketResourceProviderConfig.OAuth2SsoTicketResourceProviderConfigBuilder staticRole(String staticRole) {
         if (this.staticRoles == null) {
            this.staticRoles = new ArrayList<>();
         }

         this.staticRoles.add(staticRole);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2SsoTicketResourceProviderConfig.OAuth2SsoTicketResourceProviderConfigBuilder staticRoles(Collection<? extends String> staticRoles) {
         if (staticRoles == null) {
            throw new NullPointerException("staticRoles cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2SsoTicketResourceProviderConfig.OAuth2SsoTicketResourceProviderConfigBuilder clearStaticRoles() {
         if (this.staticRoles != null) {
            this.staticRoles.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2SsoTicketResourceProviderConfig.OAuth2SsoTicketResourceProviderConfigBuilder condition(ClaimConditionConfig condition) {
         this.condition = condition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2SsoTicketResourceProviderConfig build() {
         return new OAuth2SsoTicketResourceProviderConfig(
            this.identifier, this.ticketLifetimeInSeconds, this.encoder, switch (this.staticRoles == null ? 0 : this.staticRoles.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.staticRoles.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.staticRoles));
            }, this.condition
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2SsoTicketResourceProviderConfig.OAuth2SsoTicketResourceProviderConfigBuilder(identifier="
            + this.identifier
            + ", ticketLifetimeInSeconds="
            + this.ticketLifetimeInSeconds
            + ", encoder="
            + this.encoder
            + ", staticRoles="
            + this.staticRoles
            + ", condition="
            + this.condition
            + ")";
      }
   }
}
