package wow.doge.mygame

import cats.data.ReaderT
import monix.bio.UIO
import org.scalatest.funsuite.AnyFunSuite
import monix.execution.Scheduler.Implicits.global
import monix.bio.IO
import cats.mtl.Ask
import cats.mtl.implicits._
import cats.effect.LiftIO
import monix.bio.Task
import cats.data.Kleisli
import monix.bio.IOLift

class ReaderT_Test extends AnyFunSuite {

//   type IoReaderT[S, E, A] = ReaderT[UIO, S, Either[E, A]]
//   val IoReaderT = ReaderT
//   val t =
//     ReaderT[UIO, String, Either[Error, Unit]](s => UIO.unit.attempt)
//       .run("s")
//       .rethrow
//   val r: IoReaderT[String, Error, Unit] = IoReaderT(s => UIO.unit.attempt)
//   val t2 = r.run("s").rethrow
  // Kleisli[IO, String, Unit](s => IO.unit)

  case class Environment(str: String, num: Int)

//   test("runReaderT_Test") {
//     def fun1: ReaderT[UIO, String, Unit] = ReaderT(str => UIO(println(str)))
//     def fun2: ReaderT[UIO, Int, Unit] = ReaderT(num => UIO(println(num)))
//     def total: ReaderT[UIO, Environment, Unit] =
//       for {
//         _ <- fun1.local[Environment](_.str)
//         _ <- fun2.local[Environment](_.num)
//       } yield ()

//     val uio: UIO[Unit] = total.run(Environment("hello", 50))
//     uio.runSyncUnsafe()
//   }

  implicit val L =
    new LiftIO[ReaderT[IO[String, ?], Environment, *]] {

      override def liftIO[A](
          ioa: cats.effect.IO[A]
      ): ReaderT[IO[String, ?], Environment, A] =
        ReaderT(_ => IO.from(ioa).onErrorHandleWith(_ => IO.raiseError("whew")))

      // override def liftIO[A](ioa: cats.effect.IO[A]): monix.bio.Task[A] =
      //   Task.from(ioa)

    }

  implicit val L2 = new IOLift[ReaderT[IO[String, ?], Environment, *]] {

    override def apply[A](
        task: monix.bio.Task[A]
    ): ReaderT[IO[String, ?], Environment, A] =
      ReaderT(_ => IO.from(task).onErrorHandleWith(_ => IO.raiseError("whew")))

  }

  sealed trait AppError

  type RIO[S, E, A] = ReaderT[IO[E, ?], S, A]
  type EIO[S, A] = RIO[S, AppError, A]
  type AppIO[A] = RIO[Environment, AppError, A]

  test("2") {
    def fun1: RIO[String, String, Unit] =
      ReaderT(s => IO(println(s)).onErrorHandleWith(_ => IO.raiseError("wow")))
    def fun2: ReaderT[IO[String, ?], Int, Unit] =
      ReaderT(num =>
        IO(println(num)).onErrorHandleWith(_ => IO.raiseError("whew"))
      )
    def fun3: ReaderT[IO[String, ?], Environment, Unit] =
      for {
        env <- ReaderT.ask[IO[String, ?], Environment]
      } yield ()
    def test[F[_]]()(implicit A: Ask[F, Int]) = A.ask[Int]
    // def synctest[F[_]]
    def fun4: ReaderT[IO[String, ?], Environment, Unit] =
      for {
        env <- ReaderT.ask[IO[String, ?], Environment]
        _ <- ReaderT.liftF(
          IO(println(env)).onErrorHandleWith(_ => IO.raiseError("wow"))
        )
        _ <- fun3
      } yield ()
    val topkek =
      test[ReaderT[IO[String, ?], Int, *]]().local[Environment](_.num)
    def app: ReaderT[IO[String, ?], Environment, Unit] =
      for {
        _ <- fun1.local[Environment](_.str)
        _ <- fun2.local[Environment](_.num)
        _ <- fun3
        _ <- fun4
        _ <- topkek
      } yield ()

    val io: IO[String, Unit] = app.run(Environment("hello", 50))
    io.attempt.runSyncUnsafe()
  }

}
