package controllers.auth

import java.util.Base64

import play.api.{Configuration, Logging}
import play.api.mvc._

import scala.concurrent.{ExecutionContext, Future}
import scala.util.control.Exception.allCatch

class BasicAuthAuthenticatedAction(parser: BodyParsers.Default, appConfig: Configuration)(implicit ec: ExecutionContext)
  extends ActionBuilderImpl(parser) with Logging {

  logger.debug("In BasicAuthAuthenticatedAction")

  val BASIC_AUTH_USER = appConfig.getOptional[String]("smui.BasicAuthAuthenticatedAction.user") match {
    case Some(strUser: String) =>
      strUser
    case None =>
      logger.error(":: No value for smui.BasicAuthAuthenticatedAction.user found. Setting user to super-default.")
      "smui"
  }

  val BASIC_AUTH_PASS = appConfig.getOptional[String]("smui.BasicAuthAuthenticatedAction.pass") match {
    case Some(strUser: String) =>
      strUser
    case None =>
      logger.error(":: No value for smui.BasicAuthAuthenticatedAction.pass found. Setting pass to super-default.")
      "smui"
  }

  override def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]): Future[Result] = {

    logger.debug(s":: invokeBlock :: request.path = ${request.path}")

    /**
      * Helper method to verify, that the request is basic authenticated with configured user/pass.
      * Code is adopted from: https://dzone.com/articles/play-basic-authentication
      *
      * @param request
      * @return {{true}}, for user is authenticated.
      */
    def requestAuthenticated(request: Request[A]): Boolean = {

      request.headers.get("Authorization") match {
        case Some(authorization: String) =>
          authorization.split(" ").drop(1).headOption.filter { encoded =>
            val authInfo = new String(Base64.getDecoder().decode(encoded.getBytes)).split(":").toList
            allCatch.opt {
              val (username, password) = (authInfo.head, authInfo(1))
              username.equals(BASIC_AUTH_USER) && password.equals(BASIC_AUTH_PASS)
            } getOrElse false
          }.exists(_ => true)
        case None => false
      }
    }

    if (requestAuthenticated(request)) {
      block(request)
    } else {
      Future {
        // TODO return error JSON with authorization violation details, redirect target eventually (instead of empty 401 body)
        Results.Unauthorized("401 Unauthorized").withHeaders(("WWW-Authenticate", "Basic realm=SMUI"))
      }
    }
  }
}