package com.airlock.iam.login.app.application.configuration.oauth;

import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.application.dependency.injection.LegacyPluginInjectorFactory;
import com.airlock.iam.common.application.configuration.oauth2.OAuth2ASIdentifierConfig;
import com.airlock.iam.core.application.configuration.waf.AirlockGatewayConfig;
import com.airlock.iam.core.misc.util.ticket.Ticket;
import com.airlock.iam.core.misc.util.ticket.TicketDecoder;
import com.airlock.iam.core.misc.util.ticket.TicketEncodingException;
import com.airlock.iam.login.app.misc.RequestThreadContext;
import com.airlock.iam.login.app.misc.configuration.OAuth2AuthorizationServersConfig;
import com.airlock.iam.login.app.misc.configuration.oauth.as.OAuth2ASConfig;
import com.airlock.iam.login.app.misc.util.ServletConfigUtils;
import com.airlock.iam.oauth2.api.domain.model.AuthorizationServerId;
import com.airlock.iam.oauth2.application.service.token.OAuth2AccessTokenDecoderService;
import com.airlock.iam.oauth2.domain.model.parser.RawOAuth2TokenParser;
import com.airlock.iam.oauth2.domain.service.credential.OAuth2AccessTokenValidationService;
import com.airlock.iam.oauth2.domain.service.credential.OAuth2AccessTokenValidationServiceImpl;
import com.airlock.iam.oauth2.infrastructure.credential.OAuth2TokenBindingClientCertificateCredentialExtractor;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationContextField;
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.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional;

@Plugin(
   description = "Decodes an OAuth 2.0 Access Token to a ticket containing the username and roles. This plugin can only be used within the Loginapp.",
   displayName = "OAuth 2.0 Access Token Ticket Decoder"
)
@PluginLicenseRestriction(moduleAffiliation = "OAuthServer")
public class OAuth2AccessTokenDecoderConfig implements TicketDecoder {
   @PluginProperty(
      description = "The unique identifier of the Authorization Server.<br>This must reference an Authorization Server in the top-level OAuth 2.0 Settings of the Loginapp.",
      displayName = "OAuth 2.0 Authorization Server Reference",
      optional = false
   )
   private OAuth2ASIdentifierConfig authorizationServerReference;
   @BooleanProperty(
      description = "<p>Controls whether the Access Token's scopes are added as roles to the authenticated user.</p>",
      optional = true,
      defaultValue = "true"
   )
   private boolean addScopesAsRoles;
   @ConfigurationContextField
   private ConfigurationContext context;

   private OAuth2AccessTokenDecoderService createValidator() {
      AuthorizationServerId authorizationServerId = AuthorizationServerId.authorizationServerId(this.authorizationServerReference.getIdentifier());
      Optional<OAuth2ASConfig> asConfig = OAuth2AuthorizationServersConfig.getASConfigById(authorizationServerId, this.context);
      if (asConfig.isEmpty()) {
         throw new IllegalStateException(
            "Configuration for OAuth 2.0 authorization server with ID "
               + LogUtil.quote(authorizationServerId.id())
               + " in context "
               + this.context.getIdentifier()
               + " is required"
         );
      }

      Optional<AirlockGatewayConfig> airlockConfig = ServletConfigUtils.findGatewayConfig(this.context);
      OAuth2TokenBindingClientCertificateCredentialExtractor certificateCredentialExtractor = new OAuth2TokenBindingClientCertificateCredentialExtractor(
         getHttpServletRequest(), airlockConfig
      );
      OAuth2AccessTokenValidationService validationService = new OAuth2AccessTokenValidationServiceImpl(
         () -> asConfig.get().loginTokenService(),
         () -> asConfig.get().getUserLoader(),
         () -> asConfig.get().getClientLoader(),
         () -> certificateCredentialExtractor,
         () -> asConfig.get().getTokenBindingVerifier()
      );
      RawOAuth2TokenParser tokenParser = new RawOAuth2TokenParser(asConfig.get().tokenRepresentation());
      return new OAuth2AccessTokenDecoderService(validationService, tokenParser, this.addScopesAsRoles);
   }

   private static HttpServletRequest getHttpServletRequest() {
      try {
         return RequestThreadContext.getRequest();
      } catch (Exception var1) {
         return LegacyPluginInjectorFactory.getInjector().getInstance(HttpServletRequest.class);
      }
   }

   @Override
   public Ticket decode(String ticketString, String urlEncodingScheme) throws TicketEncodingException {
      return this.createValidator().decode(ticketString);
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public OAuth2AccessTokenDecoderConfig(OAuth2ASIdentifierConfig authorizationServerReference, boolean addScopesAsRoles, ConfigurationContext context) {
      this.authorizationServerReference = authorizationServerReference;
      this.addScopesAsRoles = addScopesAsRoles;
      this.context = context;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2AccessTokenDecoderConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private OAuth2ASIdentifierConfig authorizationServerReference;
      @SuppressFBWarnings(justification = "generated code")
      private boolean addScopesAsRoles;
      @SuppressFBWarnings(justification = "generated code")
      private ConfigurationContext context;

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

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccessTokenDecoderConfig.OAuth2AccessTokenDecoderConfigBuilder authorizationServerReference(
         OAuth2ASIdentifierConfig authorizationServerReference
      ) {
         this.authorizationServerReference = authorizationServerReference;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccessTokenDecoderConfig.OAuth2AccessTokenDecoderConfigBuilder addScopesAsRoles(boolean addScopesAsRoles) {
         this.addScopesAsRoles = addScopesAsRoles;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccessTokenDecoderConfig.OAuth2AccessTokenDecoderConfigBuilder context(ConfigurationContext context) {
         this.context = context;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2AccessTokenDecoderConfig build() {
         return new OAuth2AccessTokenDecoderConfig(this.authorizationServerReference, this.addScopesAsRoles, this.context);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2AccessTokenDecoderConfig.OAuth2AccessTokenDecoderConfigBuilder(authorizationServerReference="
            + this.authorizationServerReference
            + ", addScopesAsRoles="
            + this.addScopesAsRoles
            + ", context="
            + this.context
            + ")";
      }
   }
}
