module Yore.Schedule (schedule) where

import Control.Concurrent (forkIO, threadDelay)
import Control.Monad (when)
import Data.Functor (($>))
import Data.Time (ZonedTime, getZonedTime, nominalDiffTimeToSeconds, secondsToNominalDiffTime)
import Data.Time.Clock.POSIX (getPOSIXTime)

schedule :: (ZonedTime -> Bool) -> IO a -> IO ()
schedule shouldRunAt f = everyMinute $ \now -> when (shouldRunAt now) (f $> ())

everyMinute :: (ZonedTime -> IO ()) -> IO ()
everyMinute f = do
  -- Use POSIX time to avoid having to handle leap seconds
  now <- getPOSIXTime
  let posixSeconds :: Int
      posixSeconds = floor $ nominalDiffTimeToSeconds now
      lastFullMinute = secondsToNominalDiffTime $ fromIntegral $ posixSeconds - posixSeconds `mod` 60
      secondsSinceLastFullMinute = now - lastFullMinute
  threadDelay $ ceiling $ (60 - secondsSinceLastFullMinute) * 1_000_000
  _ <- forkIO $ f =<< getZonedTime
  everyMinute f
