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

import com.airlock.iam.base.domain.service.uri.UrlUtils;
import com.airlock.iam.common.api.domain.model.user.ProvidedUsername;
import com.airlock.iam.common.domain.model.password.PasswordImpl;
import com.airlock.iam.common.domain.service.credential.HttpBasicAuthExtractor;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.domain.model.logging.OAuth2LogTypes;
import com.airlock.iam.oauth2.domain.model.validation.OAuth2RequestHandlingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import org.apache.commons.codec.binary.Base64;

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

   private OAuth2BasicAuthHeaderUtils() {
   }

   public static String oauth2BasicAuthHeader(String userPart, String passwordPart) {
      return oauth2BasicAuthHeader(userPart, passwordPart, StandardCharsets.UTF_8);
   }

   public static String oauth2BasicAuthHeader(String userPart, String passwordPart, Charset charset) {
      return "Basic " + base64(UrlUtils.urlEncodeFormPost(userPart, charset) + ":" + UrlUtils.urlEncodeFormPost(passwordPart, charset));
   }

   public static Optional<HttpBasicAuthExtractor.BasicAuthHeader> parseOAuth2BasicAuthHeader(Optional<String> authorizationHeaderValue, Charset charset) {
      try {
         return HttpBasicAuthExtractor.extractBasicAuthHeader(authorizationHeaderValue, charset)
            .map(encodedHeader -> decodeBasicAuthHeaderValues(encodedHeader, charset));
      } catch (OAuth2RequestHandlingException e) {
         return Optional.empty();
      }
   }

   private static HttpBasicAuthExtractor.BasicAuthHeader decodeBasicAuthHeaderValues(HttpBasicAuthExtractor.BasicAuthHeader encodedHeader, Charset charset) {
      return new HttpBasicAuthExtractor.BasicAuthHeader(
         ProvidedUsername.providedUsernameOf(urlDecodeValue(encodedHeader.username().getValue(), charset)),
         PasswordImpl.passwordOf(urlDecodeValue(encodedHeader.password().getValue(), charset))
      );
   }

   private static String urlDecodeValue(String value, Charset charset) {
      try {
         return UrlUtils.urlDecode(value, charset);
      } catch (RuntimeException e) {
         LOG.warn(OAuth2LogTypes.OAUTH2_MESSAGE, "Invalid Basic-Auth header. The credential values could not be url-decoded (see RFC6749 Section 2.3.1)");
         throw new OAuth2RequestHandlingException("Invalid Basic-Auth header");
      }
   }

   private static String base64(String string) {
      return Base64.encodeBase64String(string.getBytes(StandardCharsets.UTF_8));
   }
}
