package com.airlock.iam.oauth2.domain.model.client;

import com.airlock.iam.common.api.domain.model.locking.LockInfo;
import com.airlock.iam.common.domain.model.jwt.signature.JsonWebSignatureToken;
import com.airlock.iam.common.domain.model.jwt.signature.JwsVerifier;
import com.airlock.iam.common.domain.service.jwt.signature.JwsVerifierFactory;
import com.airlock.iam.core.domain.model.http.client.HttpClient;
import com.airlock.iam.oauth2.api.domain.model.AuthorizationServerId;
import com.airlock.iam.oauth2.api.domain.model.OAuth2ClientId;
import com.airlock.iam.oauth2.api.domain.model.OAuth2ClientSecret;
import com.airlock.iam.oauth2.api.domain.model.OAuth2Scope;
import com.airlock.iam.oauth2.domain.model.AllowedScopes;
import com.airlock.iam.oauth2.domain.model.PkceCodeChallengeMethod;
import com.airlock.iam.oauth2.domain.model.client.locales.LocalizedAttribute;
import com.airlock.iam.oauth2.domain.model.clientauthentication.OAuth2ClientCertificate;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;
import java.util.Set;
import lombok.NonNull;

public final class OAuth2ClientImpl implements OAuth2Client {
   private final AuthorizationServerId authorizationServerId;
   private final OAuth2ClientId clientId;
   private final LocalizedAttribute clientName;
   private final LocalizedAttribute additionalInfo;
   private final Optional<OAuth2ClientSecret> clientSecret;
   private final Set<OAuth2ClientCertificate> clientCertificates;
   private final AllowedScopes allowedScopes;
   private final Set<OAuth2Scope> alwaysGrantedScopes;
   private final Set<String> redirectUris;
   private final Optional<String> backChannelLogoutUri;
   private final Optional<HttpClient> httpClient;
   private final Optional<LockInfo> lockInfo;
   private final Optional<JwsVerifierFactory> jwsVerifierFactory;
   private final Optional<PkceCodeChallengeMethod> pkceCodeChallengeMethod;

   public OAuth2ClientImpl(
      @NonNull AuthorizationServerId authorizationServerId,
      @NonNull OAuth2ClientId clientId,
      @NonNull LocalizedAttribute clientName,
      @NonNull LocalizedAttribute additionalInfo,
      @NonNull Optional<OAuth2ClientSecret> clientSecret,
      @NonNull Set<OAuth2ClientCertificate> clientCertificates,
      @NonNull AllowedScopes allowedScopes,
      @NonNull Set<OAuth2Scope> alwaysGrantedScopes,
      @NonNull Set<String> redirectUris,
      @NonNull Optional<String> backChannelLogoutUri,
      @NonNull Optional<HttpClient> httpClient,
      @NonNull Optional<LockInfo> lockInfo,
      @NonNull Optional<JwsVerifierFactory> jwsVerifierFactory,
      @NonNull Optional<PkceCodeChallengeMethod> pkceCodeChallengeMethod
   ) {
      if (authorizationServerId == null) {
         throw new NullPointerException("authorizationServerId is marked non-null but is null");
      }

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

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

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

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

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

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

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

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

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

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

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

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

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

      this.authorizationServerId = authorizationServerId;
      this.clientId = clientId;
      this.clientName = clientName;
      this.additionalInfo = additionalInfo;
      this.clientSecret = clientSecret;
      this.clientCertificates = clientCertificates;
      this.allowedScopes = allowedScopes;
      this.alwaysGrantedScopes = alwaysGrantedScopes;
      this.redirectUris = redirectUris;
      this.backChannelLogoutUri = backChannelLogoutUri;
      this.httpClient = httpClient;
      this.lockInfo = lockInfo;
      this.jwsVerifierFactory = jwsVerifierFactory;
      this.pkceCodeChallengeMethod = pkceCodeChallengeMethod;
   }

   @Override
   public Optional<String> defaultRedirectUri() {
      return this.redirectUris.size() == 1 ? this.redirectUris.stream().findFirst() : Optional.empty();
   }

   @Override
   public Optional<JwsVerifier> verifierFor(JsonWebSignatureToken jwt) {
      return this.jwsVerifierFactory.flatMap(factory -> factory.create(jwt));
   }

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

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public AuthorizationServerId authorizationServerId() {
      return this.authorizationServerId;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public OAuth2ClientId clientId() {
      return this.clientId;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public LocalizedAttribute clientName() {
      return this.clientName;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public LocalizedAttribute additionalInfo() {
      return this.additionalInfo;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<OAuth2ClientSecret> clientSecret() {
      return this.clientSecret;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Set<OAuth2ClientCertificate> clientCertificates() {
      return this.clientCertificates;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public AllowedScopes allowedScopes() {
      return this.allowedScopes;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Set<OAuth2Scope> alwaysGrantedScopes() {
      return this.alwaysGrantedScopes;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Set<String> redirectUris() {
      return this.redirectUris;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<String> backChannelLogoutUri() {
      return this.backChannelLogoutUri;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<HttpClient> httpClient() {
      return this.httpClient;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<LockInfo> lockInfo() {
      return this.lockInfo;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<JwsVerifierFactory> jwsVerifierFactory() {
      return this.jwsVerifierFactory;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public Optional<PkceCodeChallengeMethod> pkceCodeChallengeMethod() {
      return this.pkceCodeChallengeMethod;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2ClientImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private AuthorizationServerId authorizationServerId;
      @SuppressFBWarnings(justification = "generated code")
      private OAuth2ClientId clientId;
      @SuppressFBWarnings(justification = "generated code")
      private LocalizedAttribute clientName;
      @SuppressFBWarnings(justification = "generated code")
      private LocalizedAttribute additionalInfo;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<OAuth2ClientSecret> clientSecret;
      @SuppressFBWarnings(justification = "generated code")
      private Set<OAuth2ClientCertificate> clientCertificates;
      @SuppressFBWarnings(justification = "generated code")
      private AllowedScopes allowedScopes;
      @SuppressFBWarnings(justification = "generated code")
      private Set<OAuth2Scope> alwaysGrantedScopes;
      @SuppressFBWarnings(justification = "generated code")
      private Set<String> redirectUris;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<String> backChannelLogoutUri;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<HttpClient> httpClient;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<LockInfo> lockInfo;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<JwsVerifierFactory> jwsVerifierFactory;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<PkceCodeChallengeMethod> pkceCodeChallengeMethod;

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

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder authorizationServerId(@NonNull AuthorizationServerId authorizationServerId) {
         if (authorizationServerId == null) {
            throw new NullPointerException("authorizationServerId is marked non-null but is null");
         }

         this.authorizationServerId = authorizationServerId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder clientId(@NonNull OAuth2ClientId clientId) {
         if (clientId == null) {
            throw new NullPointerException("clientId is marked non-null but is null");
         }

         this.clientId = clientId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder clientName(@NonNull LocalizedAttribute clientName) {
         if (clientName == null) {
            throw new NullPointerException("clientName is marked non-null but is null");
         }

         this.clientName = clientName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder additionalInfo(@NonNull LocalizedAttribute additionalInfo) {
         if (additionalInfo == null) {
            throw new NullPointerException("additionalInfo is marked non-null but is null");
         }

         this.additionalInfo = additionalInfo;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder clientSecret(@NonNull Optional<OAuth2ClientSecret> clientSecret) {
         if (clientSecret == null) {
            throw new NullPointerException("clientSecret is marked non-null but is null");
         }

         this.clientSecret = clientSecret;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder clientCertificates(@NonNull Set<OAuth2ClientCertificate> clientCertificates) {
         if (clientCertificates == null) {
            throw new NullPointerException("clientCertificates is marked non-null but is null");
         }

         this.clientCertificates = clientCertificates;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder allowedScopes(@NonNull AllowedScopes allowedScopes) {
         if (allowedScopes == null) {
            throw new NullPointerException("allowedScopes is marked non-null but is null");
         }

         this.allowedScopes = allowedScopes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder alwaysGrantedScopes(@NonNull Set<OAuth2Scope> alwaysGrantedScopes) {
         if (alwaysGrantedScopes == null) {
            throw new NullPointerException("alwaysGrantedScopes is marked non-null but is null");
         }

         this.alwaysGrantedScopes = alwaysGrantedScopes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder redirectUris(@NonNull Set<String> redirectUris) {
         if (redirectUris == null) {
            throw new NullPointerException("redirectUris is marked non-null but is null");
         }

         this.redirectUris = redirectUris;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder backChannelLogoutUri(@NonNull Optional<String> backChannelLogoutUri) {
         if (backChannelLogoutUri == null) {
            throw new NullPointerException("backChannelLogoutUri is marked non-null but is null");
         }

         this.backChannelLogoutUri = backChannelLogoutUri;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder httpClient(@NonNull Optional<HttpClient> httpClient) {
         if (httpClient == null) {
            throw new NullPointerException("httpClient is marked non-null but is null");
         }

         this.httpClient = httpClient;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder lockInfo(@NonNull Optional<LockInfo> lockInfo) {
         if (lockInfo == null) {
            throw new NullPointerException("lockInfo is marked non-null but is null");
         }

         this.lockInfo = lockInfo;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder jwsVerifierFactory(@NonNull Optional<JwsVerifierFactory> jwsVerifierFactory) {
         if (jwsVerifierFactory == null) {
            throw new NullPointerException("jwsVerifierFactory is marked non-null but is null");
         }

         this.jwsVerifierFactory = jwsVerifierFactory;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl.OAuth2ClientImplBuilder pkceCodeChallengeMethod(@NonNull Optional<PkceCodeChallengeMethod> pkceCodeChallengeMethod) {
         if (pkceCodeChallengeMethod == null) {
            throw new NullPointerException("pkceCodeChallengeMethod is marked non-null but is null");
         }

         this.pkceCodeChallengeMethod = pkceCodeChallengeMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ClientImpl build() {
         return new OAuth2ClientImpl(
            this.authorizationServerId,
            this.clientId,
            this.clientName,
            this.additionalInfo,
            this.clientSecret,
            this.clientCertificates,
            this.allowedScopes,
            this.alwaysGrantedScopes,
            this.redirectUris,
            this.backChannelLogoutUri,
            this.httpClient,
            this.lockInfo,
            this.jwsVerifierFactory,
            this.pkceCodeChallengeMethod
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2ClientImpl.OAuth2ClientImplBuilder(authorizationServerId="
            + this.authorizationServerId
            + ", clientId="
            + this.clientId
            + ", clientName="
            + this.clientName
            + ", additionalInfo="
            + this.additionalInfo
            + ", clientSecret="
            + this.clientSecret
            + ", clientCertificates="
            + this.clientCertificates
            + ", allowedScopes="
            + this.allowedScopes
            + ", alwaysGrantedScopes="
            + this.alwaysGrantedScopes
            + ", redirectUris="
            + this.redirectUris
            + ", backChannelLogoutUri="
            + this.backChannelLogoutUri
            + ", httpClient="
            + this.httpClient
            + ", lockInfo="
            + this.lockInfo
            + ", jwsVerifierFactory="
            + this.jwsVerifierFactory
            + ", pkceCodeChallengeMethod="
            + this.pkceCodeChallengeMethod
            + ")";
      }
   }
}
