package com.airlock.iam.oauth2.domain.service.metadata;

import com.airlock.iam.base.domain.service.uri.UriEqualityCheck;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.domain.model.Issuer;
import com.airlock.iam.oauth2.domain.model.OAuth2IssuerValidationMode;
import com.airlock.iam.oauth2.domain.model.logging.OAuth2LogTypes;
import com.airlock.iam.oauth2.domain.model.metadata.OAuth2MetadataIssuerValidationException;
import com.google.common.annotations.VisibleForTesting;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;

public class OAuth2MetadataIssuerExtractor {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2MetadataIssuerExtractor.class);
   private final ELogger logger;

   @VisibleForTesting
   OAuth2MetadataIssuerExtractor(ELogger logger) {
      this.logger = logger;
   }

   public OAuth2MetadataIssuerExtractor() {
      this.logger = LOG;
   }

   public Issuer determineIssuer(URI providerUri, Issuer configuredIssuer, OAuth2IssuerValidationMode validationMode) throws OAuth2MetadataIssuerValidationException {
      Optional<Issuer> uriDerivedIssuer = this.tryDerivingValidIssuerId(providerUri);
      if (uriDerivedIssuer.isEmpty()) {
         this.handleValidationError(
            validationMode,
            "Could not derive valid issuer ID from metadata request URL (URL might not be https or might contain a fragment or query string): "
               + LogUtil.logSafeQuote(providerUri.toASCIIString())
         );
      } else if (!this.representSameUri(configuredIssuer, uriDerivedIssuer.get())) {
         String message = String.format(
            "Configured issuer ID %s does not match the issuer ID derived from metadata request URL %s.",
            LogUtil.quote(configuredIssuer.getValue()),
            LogUtil.logSafeQuote(uriDerivedIssuer.get().getValue())
         );
         this.handleValidationError(validationMode, message);
      }

      return configuredIssuer;
   }

   private void handleValidationError(OAuth2IssuerValidationMode validationMode, String errorMessage) throws OAuth2MetadataIssuerValidationException {
      switch (validationMode) {
         case LOG_ONLY:
            this.logger.warn(OAuth2LogTypes.OAUTH2_MESSAGE, errorMessage);
            return;
         case IGNORE:
            return;
         case FAIL:
            this.logger.warn(OAuth2LogTypes.OAUTH2_MESSAGE, errorMessage);
            throw new OAuth2MetadataIssuerValidationException(errorMessage);
         default:
            throw new IllegalStateException();
      }
   }

   private boolean representSameUri(Issuer issuer1, Issuer issuer2) {
      try {
         return UriEqualityCheck.areUrisIdentical(new URI(issuer1.getValue()), new URI(issuer2.getValue()));
      } catch (URISyntaxException e) {
         return false;
      }
   }

   private Optional<Issuer> tryDerivingValidIssuerId(URI providerUri) {
      return !Issuer.isValid(providerUri) ? Optional.empty() : Optional.of(Issuer.issuer(providerUri.toString()));
   }
}
