package gs.std.v0.effect.pipes

import cats.effect.IO
import cats.effect.unsafe.IORuntime
import fs2.Stream
import gs.std.v0.core.SHA256
import java.security.MessageDigest
import munit.*

class SHA256PipeTests extends FunSuite:

  import SHA256PipeTests.Data

  given runtime: IORuntime = IORuntime.global

  def io(
    name: String
  )(
    body: => IO[Unit]
  )(
    using
    loc: Location
  ): Unit =
    test(name)(body.unsafeRunSync())(
      using loc
    )

  def getDigest(): MessageDigest = MessageDigest.getInstance(SHA256.Algorithm)

  def shaDefault(data: String): SHA256 =
    SHA256.calculate(data)

  def shaStream(data: String): IO[SHA256] =
    Stream
      .evals(IO(data.getBytes().toList))
      .through(SHA256Pipe.apply[IO])
      .compile
      .toList
      .map {
        case sha :: Nil => sha
        case Nil        => fail("Received an empty SHA-256.")
        case _          => fail("Received multiple SHA-256.")
      }

  def readResource(resourceName: String): Stream[IO, Byte] =
    fs2.io.readInputStream(
      fis = IO(getClass().getClassLoader().getResourceAsStream(resourceName)),
      chunkSize = 1024,
      closeAfterUse = true
    )

  io("should calculate the SHA-256 for some fixed (pre-calculated) value") {
    val data        = "some-fixed-value"
    val expectedHex =
      "d5dae4f21bc27a568842bc9b467e69bd823d54d1c9e89d5ea662493068d5100d"
    val basic = shaDefault(data)

    shaStream(data).map { streamed =>
      assertEquals(basic.hex().data, expectedHex)
      assertEquals(streamed.hex().data, expectedHex)
      assert(streamed.isSame(basic))
    }
  }

  io("should calculate the SHA-256 for a 1M file") {
    readResource(Data.File1Path)
      .through(SHA256Pipe[IO])
      .compile
      .toList
      .map {
        case sha :: Nil => sha
        case Nil        => fail("Received an empty SHA-256.")
        case _          => fail("Received multiple SHA-256.")
      }
      .map(sha => assertEquals(sha.hex().data, Data.File1Sha))
  }

end SHA256PipeTests

object SHA256PipeTests:

  object Data:

    val File1Path: String = "sha256-input"

    val File1Sha: String =
      "42d96cff2300306954c166ab75862d4e5fd7ce977bb34f7e8ee785f62244d7a0"

  end Data

end SHA256PipeTests
