package com.outbrain.ob1k.security.server;

import java.util.Base64;

import com.outbrain.ob1k.Request;
import io.netty.util.CharsetUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Parses an "Authorization" header of a HTTP request
 */
public class BasicAuthenticationHeaderParser {

  private final static Logger logger = LoggerFactory.getLogger(BasicAuthenticationHeaderParser.class);

  public static final String BASIC_AUTHORIZATION_HEADER = "Authorization";
  public static final String BASIC_PREFIX = "Basic";

  /**
   * <p>
   * Extracts the credentials from the request by obtaining the Authorization header's value
   * and parsing it. Everything after the &quot;Basic&quot; prefix will be Base64 decoded and returns.
   * </p>
   * Examples:<br>
   * <pre>
   *   Authorization Header            Result
   *   ---------------------------------------------
   *   null                            null
   *   ""                              null
   *   "Basic unencoded_string"        null
   *   "Basic dXNlcjpwYXNzd29yZA=="    {@code new UsernamePasswordTuple("user", "password"}
   * </pre>
   *
   * @return the decoded credentials, or null if any error occured
   */
  public Credentials<UserPasswordToken> extractCredentials(final Request request) {
    final String basicAuthHeader = request.getHeader(BASIC_AUTHORIZATION_HEADER);
    if (StringUtils.isNotBlank(basicAuthHeader) && basicAuthHeader.startsWith(BASIC_PREFIX)) {
      return extractCredentials(basicAuthHeader);
    } else {
      return null;
    }
  }

  private Credentials<UserPasswordToken> extractCredentials(final String basicAuthHeader) {
    final String encodedCredentials = basicAuthHeader.substring(BASIC_PREFIX.length()).trim();
    if (StringUtils.isNotBlank(encodedCredentials)) {
      final String decodedCredentials = decode(encodedCredentials);

      if (StringUtils.isNotBlank(decodedCredentials)) {
        return splitCredentials(decodedCredentials);
      } else {
        return null;
      }
    } else {
      return null;
    }
  }

  //Splits a string in the form "username:password" into the UsernamePasswordTuple
  private Credentials<UserPasswordToken> splitCredentials(final String credentials) {
    final String[] splitCredentials = credentials.split(":");
    if (splitCredentials.length != 2) {
      logger.error("Error splitting credentials {}", credentials);
      return null;
    } else {
      final UserPasswordToken userPassTuple = new UserPasswordToken(splitCredentials[0],
                                                                            splitCredentials[1].toCharArray());
      return new HttpBasicCredentials(userPassTuple);
    }
  }

  private String decode(final String encodedCredentials) {
    try {
      return new String(Base64.getDecoder().decode(encodedCredentials), CharsetUtil.UTF_8);
    } catch (final Exception e) {
      logger.error("Error decoding credentials " + encodedCredentials, e);
      return null;
    }
  }

}