package tofu.concurrent

import cats.effect.concurrent.{Deferred, TryableDeferred}
import cats.effect.{Concurrent, Sync}

trait MakeDeferred[I[_], F[_]] {
  def deferred[A]: I[Deferred[F, A]]
}

trait TryableDeferreds[F[_]] extends MakeDeferred[F, F] {
  def tryable[A]: F[TryableDeferred[F, A]]
}

object Deferreds {
  def apply[F[_], A](implicit make: Deferreds[F]): F[Deferred[F, A]] = make.deferred[A]
}

object MakeDeferred extends PolymorphicMakeDefferedInstance {
  def apply[I[_], F[_], A](implicit make: MakeDeferred[I, F]): I[Deferred[F, A]] = make.deferred[A]

  def tryable[F[_], A](implicit make: TryableDeferreds[F]): F[TryableDeferred[F, A]] = make.tryable[A]

  implicit def concurrentTryableDeferreds[F[_]: Concurrent]: TryableDeferreds[F] = new TryableDeferreds[F] {
    def deferred[A]: F[Deferred[F, A]]       = Deferred[F, A]
    def tryable[A]: F[TryableDeferred[F, A]] = Deferred.tryable[F, A]
  }
}
trait PolymorphicMakeDefferedInstance {
  implicit def concurrentMakeDeferred[I[_]: Sync, F[_]: Concurrent]: MakeDeferred[I, F] = new MakeDeferred[I, F] {
    def deferred[A]: I[Deferred[F, A]] = Deferred.in[I, F, A]
  }
}