package wow.doge.mygame.subsystems.events

import scala.reflect.ClassTag

import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors
import akka.event.EventStream

/**
  * A (typed) event bus
  * Copied (and repurposed) from Akka's EventStream
  */
object EventBus {
  sealed trait Command[A]
  final case class Publish[A, E <: A](
      event: E,
      publisherName: String
  ) extends Command[A]

  final case class Subscribe[A, E <: A](subscriber: ActorRef[E])(implicit
      classTag: ClassTag[E]
  ) extends Command[A] {

    def topic: Class[_] = classTag.runtimeClass
  }

  final case class Unsubscribe[A, E <: A](subscriber: ActorRef[E])
      extends Command[A]

  def apply[A](): Behavior[EventBus.Command[A]] =
    Behaviors.setup { ctx =>
      val eventStream = new EventStream(ctx.system.classicSystem)
      new EventBus().eventStreamBehavior(eventStream)
    }

}

class EventBus[B] {
  import akka.actor.typed.scaladsl.adapter._

  private def eventStreamBehavior(
      eventStream: akka.event.EventStream
  ): Behavior[EventBus.Command[B]] =
    Behaviors.receiveMessage {
      case EventBus.Publish(event, name) =>
        eventStream.publish(event)
        Behaviors.same
      case s @ EventBus.Subscribe(subscriber) =>
        eventStream.subscribe(subscriber.toClassic, s.topic)
        Behaviors.same
      case EventBus.Unsubscribe(subscriber) =>
        eventStream.unsubscribe(subscriber.toClassic)
        Behaviors.same
    }
}
