package org.slf4j.impl

import scala.collection.immutable.ArraySeq
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

import _root_.monix.execution.Scheduler
import _root_.monix.execution.Scheduler.Implicits.global
import cats.arrow.FunctionK
import cats.effect.Clock
import cats.effect.ContextShift
import cats.effect.Effect
import cats.effect.IO
import cats.effect.Timer
import cats.implicits._
import io.odin._
import io.odin.json.Formatter
import io.odin.slf4j.OdinLoggerBinder
import io.odin.syntax._

//effect type should be specified inbefore
//log line will be recorded right after the call with no suspension
class StaticLoggerBinder extends OdinLoggerBinder[IO] {

  val ec: ExecutionContext = Scheduler.global
  implicit val timer: Timer[IO] = IO.timer(ec)
  implicit val clock: Clock[IO] = timer.clock
  implicit val cs: ContextShift[IO] = IO.contextShift(ec)
  implicit val F: Effect[IO] = IO.ioEffect

  val monixToCats = new FunctionK[_root_.monix.bio.Task, IO] {
    def apply[A](fa: _root_.monix.bio.Task[A]): IO[A] = fa.to[IO]
  }

  private lazy val (defaultConsoleLogger, release1) =
    consoleLogger[IO](minLevel = Level.Debug)
      .withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
      .allocated
      .unsafeRunSync()

  private lazy val (mainFileLogger, release2) =
    fileLogger[IO](
      "application-log-2.log",
      Formatter.json,
      minLevel = Level.Debug
    ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
      .allocated
      .unsafeRunSync()

  private lazy val (eventBusFileLogger, release3) =
    fileLogger[IO](
      "eventbus.log",
      Formatter.json,
      minLevel = Level.Debug
    ).withAsync(timeWindow = 1.milliseconds, maxBufferSize = Some(2000))
      .allocated
      .unsafeRunSync()

  {
    ArraySeq(release1, release2, release3).foreach(r =>
      sys.addShutdownHook(r.unsafeRunSync())
    )
  }

  val loggers: PartialFunction[String, Logger[IO]] = {
    case "some.external.package.SpecificClass" =>
      //disable noisy external logs
      defaultConsoleLogger.withMinimalLevel(Level.Warn)
    case asyncHttpClient
        if asyncHttpClient.startsWith("org.asynchttpclient.netty") =>
      defaultConsoleLogger.withMinimalLevel(Level.Warn)
    case s if s.startsWith("com.jayfella.jme.jfx.util.JfxPlatform") =>
      defaultConsoleLogger.withMinimalLevel(Level.Info)

    // case s
    //     if s.startsWith(
    //       "wow.doge.mygame.subsystems.movement.PlayerMovementEventHandler"
    //     ) =>
    //   defaultConsoleLogger.withMinimalLevel( Level.Trace) //selectively turn on trace logging for specific classes
    case s if s.startsWith("wow.doge.mygame.subsystems.events.EventBus") =>
      defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| eventBusFileLogger
    case s if s.startsWith("akka.actor") || s.startsWith("wow.doge.mygame") =>
      defaultConsoleLogger.withMinimalLevel(Level.Debug) |+| mainFileLogger
    case _ => //if wildcard case isn't provided, default logger is no-op
      defaultConsoleLogger.withMinimalLevel(Level.Debug)
  }
}

object StaticLoggerBinder extends StaticLoggerBinder {

  var REQUESTED_API_VERSION: String = "1.7"

  def getSingleton: StaticLoggerBinder = this

}
