package lol.http.examples

import lol.http._

import cats.effect.{ContextShift, IO, Timer}
import fs2.{Chunk, Pipe, Pull, Stream}

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

class StreamingTests extends Tests {

  implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
  implicit val timer: Timer[IO] = IO.timer(ec)
  implicit val cs: ContextShift[IO] = IO.contextShift(ec)

  def now = System.currentTimeMillis
  val `10Meg` = 10 * 1024 * 1024

  // Transform the stream into packets of 10Meg
  def rechunk: Pipe[IO,Byte,Chunk[Byte]] =
    _.repeatPull(_.unconsN(`10Meg`, true).flatMap {
      case Some((chunks, h)) =>
        Pull.output1(chunks) as Some(h)
      case None =>
        Pull.pure(None)
    })

  test("Slow server read", Slow) {
    withServer(Server.listen() { req =>
      val start = now
      // Read at most 3Meg per second
      req.read(
        _.through(rechunk).
          evalMap(c => IO(println(s"${c.size} bytes received"))).
          flatMap(_ => Stream.sleep[IO](3.seconds))
          .compile.drain
      ).map { _ =>
        Ok(s"${now - start}")
      }
    }) { server =>
      val start = now
      var end = 0:Long

      // Send 100M as fast as possible
      val timeToReceive = contentString(
        Post(
          s"http://localhost:${server.port}/",
          content = Content(
            stream = Stream.eval(IO {
              println(s"${`10Meg`} bytes sent")
              end = now
              Chunk.bytes(("." * `10Meg`).getBytes)
            }).repeat.take(10).flatMap(c => Stream.chunk(c))
          )
        ).addHeaders(h"Content-Length" -> h"${10 * `10Meg`}"),
        atMost = 2.minutes
      ).toInt
      val timeToSend = (end - start).toInt

      println(s"Received in ${timeToReceive/1000}s")
      println(s"Sent in ${timeToSend/1000}s")

      timeToReceive should be > 25000
      timeToSend should be > 15000
    }
  }
  test("Client read compressed", Slow) {
    withServer(Server.listen() { req =>
      Ok(Content(Stream.eval(IO {
        println(s"sent ${`10Meg`} bytes")
        Chunk.bytes(("." * `10Meg`).getBytes)
      }).repeat.take(10).flatMap(c => Stream.chunk(c))))
        .addHeaders(h"Content-Length" -> h"${10 * `10Meg`}")
    }) { server =>
      await(atMost = 2.minutes) {
        Client("localhost", server.port).runAndStop { client =>
          for {
            length1 <- client.run(Get("/a"))(_.readSuccess { stream =>
              stream.chunks.map(_.size).compile.fold(0)(_ + _)
            })
            length2 <- client.run(Get("/b").addHeaders(h"Accept-Encoding" -> h"gzip"))(_.readSuccess { stream =>
              stream.chunks.map(_.size).compile.fold(0)(_ + _)
            })
            length3 <- client.run(Get("/c").addHeaders(h"Accept-Encoding" -> h"deflate"))(_.readSuccess { stream =>
              stream.chunks.map(_.size).compile.fold(0)(_ + _)
            })
          } yield {
            length1 shouldEqual 10 * `10Meg`
            length2 shouldEqual length1
            length3 shouldEqual length1
          }
        }
      }
    }
  }

}