package com.criteo.slab.lib

import java.net._
import java.time.Duration
import java.util.concurrent._

import com.criteo.slab.core.Context
import com.criteo.slab.helper.FutureTests
import com.criteo.slab.lib.Values.Latency
import com.criteo.slab.lib.graphite.{DataPoint, GraphiteMetric, GraphiteStore}
import org.scalatest.{FlatSpec, Matchers}

import scala.io._
import com.criteo.slab.lib.graphite.GraphiteCodecs._

class GraphiteStoreSpec extends FlatSpec with Matchers with FutureTests {
  val port = 5000
  val server = new ServerSocket(port)
  val store = new GraphiteStore("localhost", port, "http://localhost", Duration.ofSeconds(60))

  val pool = Executors.newFixedThreadPool(1)

  "value store" should "be able to send metrics to Graphite server" in {
    val f = pool.submit(new Echo(server))
    store.upload("metrics", Context.now, Latency(200))
    f.get should startWith("metrics.latency 200")
  }

  "upload" should "returns the exception when failed" in {
    whenReady(
      store.upload("metrics", Context.now, Latency(200)).failed
    ) { res =>
      res shouldBe a[java.net.ConnectException]
    }
  }

  "transform metrics" should "turn Graphite metrics into pairs" in {
    val metrics = List(
      GraphiteMetric(
        "metric.one",
        List(
          DataPoint(None, 2000),
          DataPoint(Some(1), 2060)
        )
      ),
      GraphiteMetric(
        "metric.two",
        List(
          DataPoint(None, 2000),
          DataPoint(Some(2), 2060)
        )
      )
    )
    GraphiteStore.transformMetrics("metric", metrics) shouldEqual Map("one" -> 1.0, "two" -> 2.0)
  }

  "transform metrics" should "return empty if some metrics are missing" in {
    val metrics = List(
      GraphiteMetric(
        "metric.one",
        List(DataPoint(Some(1), 2000))
      ),
      GraphiteMetric(
        "metric.two",
        List(DataPoint(None, 2000))
      )
    )
    GraphiteStore.transformMetrics("metric", metrics) shouldEqual Map.empty
  }

  "group metrics" should "group metrics" in {
    val metrics = List(
      GraphiteMetric(
        "metric.one",
        List(DataPoint(Some(1), 1000), DataPoint(Some(2), 2000))
      ),
      GraphiteMetric(
        "metric.two",
        List(DataPoint(Some(3), 1000), DataPoint(Some(4), 2000))
      )
    )
    GraphiteStore.groupMetrics("metric", metrics) shouldEqual Map(
      1000000 -> Map("one" -> 1, "two" -> 3),
      2000000 -> Map("one" -> 2, "two" -> 4)
    )
  }

  class Echo(server: ServerSocket) extends Callable[String] {
    def call() = {
      val s = server.accept
      val lines = new BufferedSource(s.getInputStream).getLines
      val result = lines.mkString
      s.close
      server.close
      result
    }
  }

}