package ee.cone.c4gate

import java.io.FileNotFoundException
import java.util.UUID

import com.typesafe.scalalogging.LazyLogging
import ee.cone.c4actor.{Config, NanoTimer, RawSnapshot, RawSnapshotLoader, RawSnapshotLoaderFactory, RemoteSnapshotUtil, SnapshotMaker, SnapshotTask, SnapshotTaskSigner}
import ee.cone.c4di.{c4, c4multi, provide}
import okio.ByteString

import scala.annotation.tailrec

@c4multi("RemoteRawSnapshotLoaderImplApp") final class RemoteRawSnapshotLoaderImpl(baseURL: String)(util: HttpUtil) extends RawSnapshotLoader with LazyLogging {
  def load(snapshot: RawSnapshot): ByteString = {
    val tm = NanoTimer()
    val resp = util.get(s"$baseURL/${snapshot.relativePath}", Nil)
    assert(resp.status == 200)
    logger.debug(s"downloaded ${resp.body.size} in ${tm.ms} ms")
    resp.body
  }
}


@c4("MergingSnapshotApp") final class RemoteRawSnapshotLoaderFactory(inner: RemoteRawSnapshotLoaderImplFactory) extends RawSnapshotLoaderFactory {
  def create(baseURL: String): RawSnapshotLoader = inner.create(baseURL)
}

@c4("RemoteRawSnapshotApp") final class RemoteSnapshotUtilImpl(util: HttpUtil) extends RemoteSnapshotUtil with LazyLogging {
  def authHeaders(signed: String): List[(String, String)] =
    List(("x-r-signed", signed))
  def measure[R](f: =>R): R = {
    val startTime = System.currentTimeMillis
    val res = f
    logger.debug(s"Snapshot request time: ${System.currentTimeMillis - startTime}")
    res
  }
  def request(appURL: String, signed: String): ()=>List[RawSnapshot] = measure{
    val url: String = "/need-snapshot"
    val uuid = UUID.randomUUID().toString
    util.post(s"$appURL$url", ("x-r-response-key",uuid) :: authHeaders(signed))
    () =>
      @tailrec def retry(): HttpResponse =
        try {
          val res = util.get(s"$appURL/response/$uuid",Nil)
          if(res.status!=200) throw new FileNotFoundException
          res
        } catch {
          case e: FileNotFoundException =>
            Thread.sleep(1000)
            retry()
        }
      val headers = retry().headers
      headers.getOrElse("x-r-snapshot-keys",Nil) match {
        case Seq(res) => res.split(",").map(RawSnapshot).toList
        case _ => throw new Exception(headers.getOrElse("x-r-error-message",Nil).mkString(";"))
      }
  }
}

class RemoteSnapshotAppURL(val value: String)

@c4("RemoteRawSnapshotApp") final class DefRemoteSnapshotAppURL(config: Config) extends RemoteSnapshotAppURL(config.get("C4HTTP_SERVER"))

@c4("RemoteRawSnapshotApp") final class EnvRemoteRawSnapshotLoader(url: RemoteSnapshotAppURL, factory: RemoteRawSnapshotLoaderImplFactory) {
  @provide def get: Seq[RawSnapshotLoader] = List(factory.create(url.value))
}

@c4("RemoteRawSnapshotApp") final class RemoteSnapshotMaker(
  appURL: RemoteSnapshotAppURL, util: RemoteSnapshotUtil, signer: SnapshotTaskSigner
) extends SnapshotMaker {
  def make(task: SnapshotTask): List[RawSnapshot] =
    util.request(appURL.value, signer.sign(task, System.currentTimeMillis() + 3600*1000))()
}