
package com.hotels.styx

import java.net.InetSocketAddress
import java.util
import java.util.Collections._
import java.util.concurrent.TimeUnit.MILLISECONDS

import com.google.common.collect.ImmutableMap
import com.hotels.styx.admin.AdminServerConfig
import com.hotels.styx.api.HttpInterceptor.Chain
import com.hotels.styx.api.configuration.Configuration.MapBackedConfiguration
import com.hotels.styx.api.extension.service.spi.StyxService
import com.hotels.styx.api.plugins.spi.{Plugin, PluginFactory}
import com.hotels.styx.api._
import com.hotels.styx.config.Config
import com.hotels.styx.metrics.StyxMetrics
import com.hotels.styx.proxy.ProxyServerConfig
import com.hotels.styx.server.netty.NettyServerConfig.Connectors
import com.hotels.styx.server.{HttpConnectorConfig, HttpsConnectorConfig}
import com.hotels.styx.startup.StyxServerComponents
import com.hotels.styx.support.CodaHaleMetricsFacade
import com.hotels.styx.support.configuration.StyxBaseConfig

import scala.collection.JavaConverters._

object StyxServerSupport {
  def newHttpConnConfig(port: Int): HttpConnectorConfig = new HttpConnectorConfig(port)

  def newHttpsConnConfig(port: Int): HttpsConnectorConfig = new HttpsConnectorConfig.Builder()
    .sslProvider("JDK")
    .port(port)
    .build()

  def newHttpsConnConfig(conf: HttpsConnectorConfig): HttpsConnectorConfig = {
    val builder = new HttpsConnectorConfig.Builder()
      .sslProvider(conf.sslProvider)
      .port(conf.port())
      .sessionCacheSize(conf.sessionCacheSize)
      .sessionTimeout(conf.sessionTimeoutMillis, MILLISECONDS)

    if (conf.certificateFile != null) {
      builder.certificateFile(conf.certificateFile)
    }
    if (conf.certificateKeyFile != null) {
      builder.certificateKeyFile(conf.certificateKeyFile)
    }

    builder.build()
  }

  def newAdminServerConfigBuilder(adminHttpConnConfig: HttpConnectorConfig) = {
    new AdminServerConfig.Builder()
      .setBossThreadsCount(1)
      .setWorkerThreadsCount(1)
      .setHttpConnector(adminHttpConnConfig)
  }

  def newProxyServerConfigBuilder(httpConnConfig: HttpConnectorConfig, httpsConnConfig: HttpsConnectorConfig) = {
    new ProxyServerConfig.Builder()
      .setConnectors(new Connectors(httpConnConfig, httpsConnConfig))
  }

  def newStyxConfig(yaml: String, proxyServerConfigBuilder: ProxyServerConfig.Builder, adminServerConfigBuilder: AdminServerConfig.Builder) = {
    new StyxConfig(new MapBackedConfiguration(Config.config(yaml))
      .set("proxy", proxyServerConfigBuilder.build())
      .set("admin", adminServerConfigBuilder.build()))
  }

  def serverComponents(styxConfig: StyxConfig, styxService: StyxService, plugins: Map[String, Plugin] = Map.empty, pluginFactories : List[PluginFactory] = Nil) = {
    val plugins1 = plugins.asJava

    val builder = new StyxServerComponents.Builder()
      .styxConfig(styxConfig)
      .additionalServices(ImmutableMap.of("backendServiceRegistry", styxService))

    if (plugins.nonEmpty) {
      builder.plugins(plugins1)
    } else {
      builder
    }
  }

  def newCoreConfig(styxConfig: StyxConfig, plugins: Map[String, Plugin]) = {
    val plugins1 = plugins.asJava

    val builder = new StyxServerComponents.Builder()
      .styxConfig(styxConfig)

    if (plugins.nonEmpty) {
      builder.plugins(plugins1)
    } else {
      builder
    }
  }

  def noOp[T] = (x: T) => x

  //  implicit class StyxServerOperations(val styxServer: StyxServer) extends StyxServerSupplements
}

trait StyxServerSupport {
  implicit class StyxServerOperations(val styxServer: StyxServer) extends StyxServerSupplements
}

trait StyxServerSupplements {
  val styxServer: StyxServer

  private def toHostAndPort(address: InetSocketAddress) = address.getHostName + ":" + address.getPort

  def httpsProxyHost: String = toHostAndPort(styxServer.proxyHttpsAddress())

  def proxyHost: String = toHostAndPort(styxServer.proxyHttpAddress())

  def adminHost: String = toHostAndPort(styxServer.adminHttpAddress())

  def secureRouterURL(path: String) = s"https://$httpsProxyHost$path"

  def routerURL(path: String) = s"http://$proxyHost$path"

  def adminURL(path: String) = s"http://$adminHost$path"

  def secureHttpPort = portNumberOrElse(styxServer.proxyHttpsAddress())

  def httpPort = portNumberOrElse(styxServer.proxyHttpAddress())

  def adminPort = portNumberOrElse(styxServer.adminHttpAddress())

  def metricsSnapshot = {
    val adminHostName = styxServer.adminHttpAddress().getHostName
    new CodaHaleMetricsFacade(StyxMetrics.downloadFrom(adminHostName, adminPort))
  }

  private def portNumberOrElse(address: InetSocketAddress) = Option(address)
    .map(_.getPort)
    .orElse(Some(-1))
    .get
}

class PluginAdapter extends Plugin {
  override def adminInterfaceHandlers(): util.Map[String, HttpHandler] = emptyMap()

  override def intercept(request: LiveHttpRequest, chain: Chain): Eventual[LiveHttpResponse] = chain.proceed(request)

  override def styxStarting(): Unit = Unit

  override def styxStopping(): Unit = Unit
}
