package loci
package communicator
package ws.akka

import java.security.cert.Certificate

import akka.http.scaladsl.model.{HttpMessage, HttpRequest, HttpResponse, Uri}
import akka.http.scaladsl.model.headers._
import akka.http.scaladsl.model.ws.WebSocketRequest

private case class WSSecurityProperties(
  isAuthenticated: Boolean, isProtected: Boolean, isEncrypted: Boolean,
  certificates: Seq[Certificate])

private object WSSecurityProperties {
  final val HTTPS = "https"
  final val WSS = "wss"
  final val NoProtocol = "NONE" // TLSv1, etc.
  final val NoCipher = "SSL_NULL_WITH_NULL_NULL" // see RFC2246, RFC3268, etc.
  final val NoEncryptionFragment = "WITH_NULL"

  def apply(request: WebSocketRequest, response: HttpResponse,
      authenticated: Boolean): WSSecurityProperties =
    create(request.uri, response, authenticated)

  def apply(request: HttpRequest, authenticated: Boolean): WSSecurityProperties =
    create(request.uri, request, authenticated)

  private def create(uri: Uri, message: HttpMessage, authenticated: Boolean)
    : WSSecurityProperties = {

    val tls = uri.scheme == HTTPS || uri.scheme == WSS

    val properties = message.header[`Tls-Session-Info`] map { info =>
      val protocol = info.session.getProtocol
      val cipher = info.session.getCipherSuite

      val tls = protocol != NoProtocol && cipher != NoCipher

      val certificates = info.peerCertificates
      val isAuthenticated = tls && certificates.nonEmpty
      val isProtected = tls
      val isEncrypted = tls && !(cipher contains NoEncryptionFragment)

      WSSecurityProperties(authenticated || isAuthenticated, isProtected, isEncrypted, certificates)
    }

    properties getOrElse { WSSecurityProperties(authenticated, tls, tls, Seq.empty) }
  }
}