package com.airlock.iam.techclientreg.domain.model.registration;

import com.airlock.iam.techclientreg.api.domain.model.oauth2.OAuth2ClientRegistrationMetadata;
import com.google.common.base.Functions;
import com.nimbusds.langtag.LangTag;
import com.nimbusds.oauth2.sdk.ResponseType;
import com.nimbusds.oauth2.sdk.Scope;
import com.nimbusds.oauth2.sdk.client.ClientMetadata;
import com.nimbusds.oauth2.sdk.id.Identifier;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;

public final class OAuth2ClientRegistrationMetadataImpl implements OAuth2ClientRegistrationMetadata {
   private final ClientMetadata metadata;

   public OAuth2ClientRegistrationMetadataImpl(ClientMetadata metadata) {
      this.metadata = metadata;
   }

   @Override
   public Optional<String> clientName() {
      return Optional.ofNullable(this.metadata.getName());
   }

   @Override
   public Map<Locale, String> clientNameTranslations() {
      return collectByLocale(this.metadata.getNameEntries());
   }

   @Override
   public Set<URI> redirectUris() {
      return Optional.ofNullable(this.metadata.getRedirectionURIs()).orElse(Set.of());
   }

   @Override
   public Optional<String> tokenEndpointAuthMethod() {
      return Optional.ofNullable(this.metadata.getTokenEndpointAuthMethod()).map(Identifier::getValue);
   }

   @Override
   public List<String> grantTypes() {
      return Optional.ofNullable(this.metadata.getGrantTypes())
         .stream()
         .flatMap(Collection::stream)
         .<String>map(Identifier::getValue)
         .collect(Collectors.toList());
   }

   @Override
   public List<String> responseTypes() {
      return Optional.ofNullable(this.metadata.getResponseTypes())
         .stream()
         .flatMap(Collection::stream)
         .<String>map(ResponseType::toString)
         .collect(Collectors.toList());
   }

   @Override
   public Optional<URI> clientUri() {
      return Optional.ofNullable(this.metadata.getURI());
   }

   @Override
   public Map<Locale, URI> clientUriTranslations() {
      return collectByLocale(this.metadata.getURIEntries());
   }

   @Override
   public Optional<URI> logoUri() {
      return Optional.ofNullable(this.metadata.getLogoURI());
   }

   @Override
   public Map<Locale, URI> logoUriTranslations() {
      return collectByLocale(this.metadata.getLogoURIEntries());
   }

   @Override
   public List<String> scopes() {
      return Optional.ofNullable(this.metadata.getScope()).<List<String>>map(Scope::toStringList).orElse(List.of());
   }

   @Override
   public List<String> contacts() {
      return Optional.ofNullable(this.metadata.getEmailContacts()).orElse(List.of());
   }

   @Override
   public Optional<URI> tosUri() {
      return Optional.ofNullable(this.metadata.getTermsOfServiceURI());
   }

   @Override
   public Map<Locale, URI> tosUriTranslations() {
      return collectByLocale(this.metadata.getTermsOfServiceURIEntries());
   }

   @Override
   public Optional<URI> policyUri() {
      return Optional.ofNullable(this.metadata.getPolicyURI());
   }

   @Override
   public Map<Locale, URI> policyUriTranslations() {
      return collectByLocale(this.metadata.getPolicyURIEntries());
   }

   @Override
   public Optional<String> softwareId() {
      return Optional.ofNullable(this.metadata.getSoftwareID()).map(Identifier::getValue);
   }

   @Override
   public Optional<String> softwareVersion() {
      return Optional.ofNullable(this.metadata.getSoftwareVersion()).map(Identifier::getValue);
   }

   @Override
   public Optional<String> getCustomStringClaim(String claimName) {
      return this.metadata.getCustomField(claimName) instanceof String s ? Optional.ofNullable(StringUtils.trimToNull(s)) : Optional.empty();
   }

   private static <T> Map<Locale, T> collectByLocale(Map<LangTag, T> entries) {
      return entries.entrySet()
         .stream()
         .filter(e -> e.getKey() != null && e.getValue() != null)
         .collect(Collectors.toMap(Functions.compose(OAuth2ClientRegistrationMetadataImpl::toLocale, Entry::getKey), Entry::getValue));
   }

   private static Locale toLocale(LangTag langTag) {
      return Locale.forLanguageTag(langTag.toString());
   }

   @SuppressFBWarnings(justification = "generated code")
   public ClientMetadata getMetadata() {
      return this.metadata;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof OAuth2ClientRegistrationMetadataImpl other)) {
         return false;
      } else {
         Object this$metadata = this.metadata;
         Object other$metadata = other.metadata;
         return this$metadata == null ? other$metadata == null : this$metadata.equals(other$metadata);
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public int hashCode() {
      int PRIME = 59;
      int result = 1;
      Object $metadata = this.metadata;
      return result * 59 + ($metadata == null ? 43 : $metadata.hashCode());
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public String toString() {
      return (R)("OAuth2ClientRegistrationMetadataImpl(metadata=" + this.getMetadata() + ")");
   }
}
