package ch_03

import scala.concurrent.duration.*
import cats.syntax.parallel.*
import cats.effect.*
import utils.*

def unsafeCounter(words: List[String]): IO[Unit] =
  var total = 0

  def task(word: String): IO[Unit] =
    val wordCount = word.split(" ").length
    for
      _ <- IO(s"$word -> $wordCount").inspect
      _ <- IO(total += wordCount)
      _ <- IO(s"current total: $total").inspect
    yield ()

  words.map(task).parSequence.void

def safeCounter(words: List[String]): IO[Unit] =

  def task(word: String, total: Ref[IO, Int]): IO[Unit] =
    val wordCount = word.split(" ").length
    for
      _        <- IO(s"$word -> $wordCount").inspect
      newTotal <- total.updateAndGet(_ + wordCount)
      _        <- IO(s"current total: $newTotal").inspect
    yield ()

  for
    ref <- IO.ref(0)
    _   <- words.map(task(_, ref)).parSequence
  yield ()

// ex. refactor to IO.ref
def unsafeTickingClock(interval: FiniteDuration): IO[Unit] =
  var ticks = 0L

  def tickClock: IO[Unit] =
    for
      _ <- IO.sleep(1.second)
      _ <- IO(ticks += 1)
      _ <- tickClock
    yield ()

  def showTickingClock(interval: FiniteDuration): IO[Unit] =
    for
      _ <- IO.sleep(interval)
      _ <- IO(s"ticks: $ticks").inspect
      _ <- showTickingClock(interval)
    yield ()

  (tickClock, showTickingClock(interval)).parTupled.void

def safeTickingClock(interval: FiniteDuration): IO[Unit] =

  def tickClock(ref: Ref[IO, Long]): IO[Unit] =
    for
      _ <- IO.sleep(1.second)
      _ <- ref.update(_ + 1)
      _ <- tickClock(ref)
    yield ()

  def showTickingClock(interval: FiniteDuration, ref: Ref[IO, Long]): IO[Unit] =
    for
      _ <- IO.sleep(interval)
      _ <- ref.get.map(ticks => s"ticks: $ticks").inspect
      _ <- showTickingClock(interval, ref)
    yield ()

  for
    ref                 <- IO.ref(0L)
    fibTickClock        <- tickClock(ref).start
    fibShowTickingClock <- showTickingClock(interval, ref).start
    _                   <- fibTickClock.join
    _                   <- fibShowTickingClock.join
  yield ()