module Polysemy.Hasql.Queue.Output where

import Data.UUID (UUID)
import Exon (exon)
import Polysemy.Db.Data.DbError (DbError)
import Polysemy.Db.Effect.Random (Random, random)
import qualified Polysemy.Db.Effect.Store as Store
import Polysemy.Db.Effect.Store (Store)
import qualified Log
import Polysemy.Output (Output (Output))
import qualified Time as Time
import Time (Seconds (Seconds))
import Prelude hiding (Queue, group)
import Sqel.Data.Sql (Sql (Sql))
import Sqel.Data.Uid (Uid (Uid))
import Sqel.SOP.Constraint (symbolText)

import qualified Polysemy.Hasql.Data.QueueOutputError as QueueOutputError
import Polysemy.Hasql.Data.QueueOutputError (QueueOutputError)
import qualified Polysemy.Hasql.Database as Database (retryingSql)
import Polysemy.Hasql.Effect.Database (Database)
import Polysemy.Hasql.Queue.Data.Queued (Queued (Queued))

-- TODO I think notify doesn't even need a unique connection, only listen does
interpretOutputQueueDb ::
   (queue :: Symbol) d t dt r .
  KnownSymbol queue =>
  Members [Store UUID (Queued t d) !! DbError, Database !! DbError, Time t dt, Log, Random UUID, Embed IO] r =>
  InterpreterFor (Output d !! QueueOutputError) r
interpretOutputQueueDb :: forall (queue :: Symbol) d t dt (r :: EffectRow).
(KnownSymbol queue,
 Members
   '[Store UUID (Queued t d) !! DbError, Database !! DbError,
     Time t dt, Log, Random UUID, Embed IO]
   r) =>
InterpreterFor (Output d !! QueueOutputError) r
interpretOutputQueueDb =
  forall err (eff :: (* -> *) -> * -> *) (r :: EffectRow).
FirstOrder eff "interpretResumable" =>
(forall x (r0 :: EffectRow).
 eff (Sem r0) x -> Sem (Stop err : r) x)
-> InterpreterFor (Resumable err eff) r
interpretResumable \case
    Output d
d -> do
      UUID
id' <- forall a (r :: EffectRow). Member (Random a) r => Sem r a
random
      t
created <- forall t d (r :: EffectRow). Member (Time t d) r => Sem r t
Time.now
      forall err (eff :: (* -> *) -> * -> *) err' (r :: EffectRow) a.
Members '[Resumable err eff, Stop err'] r =>
(err -> err') -> Sem (eff : r) a -> Sem r a
resumeHoist DbError -> QueueOutputError
QueueOutputError.Insert do
        forall (f :: * -> *) i d (r :: EffectRow).
Member (QStore f i d) r =>
d -> Sem r ()
Store.insert (forall i a. i -> a -> Uid i a
Uid UUID
id' (forall t a. t -> a -> Queued t a
Queued t
created d
d))
      forall (r :: EffectRow).
(HasCallStack, Member Log r) =>
Text -> Sem r ()
Log.debug [exon|executing `notify` for queue '#{symbolText @queue}'|]
      forall err (eff :: (* -> *) -> * -> *) err' (r :: EffectRow) a.
Members '[Resumable err eff, Stop err'] r =>
(err -> err') -> Sem (eff : r) a -> Sem r a
resumeHoist DbError -> QueueOutputError
QueueOutputError.Notify do
        forall t (r :: EffectRow).
(TimeUnit t, Member Database r) =>
t -> Sql -> Sem r ()
Database.retryingSql (Int64 -> Seconds
Seconds Int64
3) (UUID -> Sql
sql UUID
id')
      where
        sql :: UUID -> Sql
sql UUID
id' =
          [exon|notify "#{Sql (symbolText @queue)}", '#{show id'}'|]