module Polysemy.Hasql.Queue.Store where

import Data.UUID (UUID)
import Generics.SOP (NP (Nil, (:*)))
import Polysemy.Db.Data.DbError (DbError)
import Polysemy.Db.Effect.Store (Store)
import Prelude hiding (Queue, listen)
import Sqel.Codec (PrimColumn)
import Sqel.Comp (CompName)
import Sqel.Data.QuerySchema (QuerySchema)
import Sqel.Data.TableSchema (TableSchema)
import Sqel.Data.Uid (Uuid)
import Sqel.PgType (tableSchema)
import qualified Sqel.Prim as Sqel
import Sqel.Prim (prim, primAs)
import Sqel.Product (prod)
import Sqel.Query (checkQuery)
import Sqel.Uid (uid)

import Polysemy.Hasql.Effect.Database (Database)
import Polysemy.Hasql.Interpreter.DbTable (interpretTable)
import Polysemy.Hasql.Interpreter.Store (interpretStoreDb)
import Polysemy.Hasql.Queue.Data.Queued (Queued)
import Sqel.Data.Sel (TSel(TSel))

class StoreTable t d where
  storeTable :: (TableSchema (Uuid (Queued t d)), QuerySchema UUID (Uuid (Queued t d)))

instance (
    ToJSON d,
    FromJSON d,
    PrimColumn t,
    CompName d ('TSel prefix name),
    KnownSymbol (AppendSymbol "Queued" name)
  ) => StoreTable t d where
  storeTable :: (TableSchema (Uuid (Queued t d)),
 QuerySchema UUID (Uuid (Queued t d)))
storeTable =
    (TableSchema
  (DdType
     ('DdK
        'SelAuto
        '[]
        (Uuid (Queued t d))
        ('Comp
           ('TSel 'NoPrefix (AppendSymbol "Queued" name))
           ('Prod 'Reg)
           'Nest
           '[ 'DdK ('SelSymbol "id") '[] UUID 'Prim,
              'DdK
                'SelAuto
                '[]
                (Queued t d)
                ('Comp
                   ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                   ('Prod 'Reg)
                   'Merge
                   '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                      'DdK
                        ('SelSymbol "queue_payload")
                        '[PgPrimName, PrimValueCodec d]
                        d
                        'Prim])])))
ts, QuerySchema
  (DdType ('DdK ('SelSymbol "id") '[] UUID 'Prim))
  (DdType
     ('DdK
        'SelAuto
        '[]
        (Uuid (Queued t d))
        ('Comp
           ('TSel 'NoPrefix (AppendSymbol "Queued" name))
           ('Prod 'Reg)
           'Nest
           '[ 'DdK ('SelSymbol "id") '[] UUID 'Prim,
              'DdK
                'SelAuto
                '[]
                (Queued t d)
                ('Comp
                   ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                   ('Prod 'Reg)
                   'Merge
                   '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                      'DdK
                        ('SelSymbol "queue_payload")
                        '[PgPrimName, PrimValueCodec d]
                        d
                        'Prim])])))
qs)
    where
      ts :: TableSchema
  (DdType
     ('DdK
        'SelAuto
        '[]
        (Uuid (Queued t d))
        ('Comp
           ('TSel 'NoPrefix (AppendSymbol "Queued" name))
           ('Prod 'Reg)
           'Nest
           '[ 'DdK ('SelSymbol "id") '[] UUID 'Prim,
              'DdK
                'SelAuto
                '[]
                (Queued t d)
                ('Comp
                   ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                   ('Prod 'Reg)
                   'Merge
                   '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                      'DdK
                        ('SelSymbol "queue_payload")
                        '[PgPrimName, PrimValueCodec d]
                        d
                        'Prim])])))
ts = forall (table :: DdK).
MkTableSchema table =>
Dd table -> TableSchema (DdType table)
tableSchema forall {i}.
Dd
  ('DdK
     'SelAuto
     '[]
     (Uid i (Queued t d))
     ('Comp
        ('TSel 'NoPrefix (AppendSymbol "Queued" name))
        ('Prod 'Reg)
        'Nest
        '[ 'DdK ('SelSymbol "id") '[] i 'Prim,
           'DdK
             'SelAuto
             '[]
             (Queued t d)
             ('Comp
                ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                ('Prod 'Reg)
                'Merge
                '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                   'DdK
                     ('SelSymbol "queue_payload")
                     '[PgPrimName, PrimValueCodec d]
                     d
                     'Prim])]))
table
      qs :: QuerySchema
  (DdType ('DdK ('SelSymbol "id") '[] UUID 'Prim))
  (DdType
     ('DdK
        'SelAuto
        '[]
        (Uuid (Queued t d))
        ('Comp
           ('TSel 'NoPrefix (AppendSymbol "Queued" name))
           ('Prod 'Reg)
           'Nest
           '[ 'DdK ('SelSymbol "id") '[] UUID 'Prim,
              'DdK
                'SelAuto
                '[]
                (Queued t d)
                ('Comp
                   ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                   ('Prod 'Reg)
                   'Merge
                   '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                      'DdK
                        ('SelSymbol "queue_payload")
                        '[PgPrimName, PrimValueCodec d]
                        d
                        'Prim])])))
qs = forall (query :: DdK) (table :: DdK).
CheckQuery query table =>
Dd query -> Dd table -> QuerySchema (DdType query) (DdType table)
checkQuery forall {a}. Dd ('DdK ('SelSymbol "id") '[] a 'Prim)
query forall {i}.
Dd
  ('DdK
     'SelAuto
     '[]
     (Uid i (Queued t d))
     ('Comp
        ('TSel 'NoPrefix (AppendSymbol "Queued" name))
        ('Prod 'Reg)
        'Nest
        '[ 'DdK ('SelSymbol "id") '[] i 'Prim,
           'DdK
             'SelAuto
             '[]
             (Queued t d)
             ('Comp
                ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                ('Prod 'Reg)
                'Merge
                '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                   'DdK
                     ('SelSymbol "queue_payload")
                     '[PgPrimName, PrimValueCodec d]
                     d
                     'Prim])]))
table
      query :: Dd ('DdK ('SelSymbol "id") '[] a 'Prim)
query = forall (name :: Symbol) a.
KnownSymbol name =>
Dd ('DdK ('SelSymbol name) '[] a 'Prim)
primAs @"id"
      table :: Dd
  ('DdK
     'SelAuto
     '[]
     (Uid
        i
        (DdType
           ('DdK
              'SelAuto
              '[]
              (Queued t d)
              ('Comp
                 ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                 ('Prod 'Reg)
                 'Nest
                 '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                    'DdK
                      ('SelSymbol "queue_payload")
                      '[PgPrimName, PrimValueCodec d]
                      d
                      'Prim]))))
     ('Comp
        (DdTypeSel
           ('DdK
              'SelAuto
              '[]
              (Queued t d)
              ('Comp
                 ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                 ('Prod 'Reg)
                 'Nest
                 '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                    'DdK
                      ('SelSymbol "queue_payload")
                      '[PgPrimName, PrimValueCodec d]
                      d
                      'Prim])))
        ('Prod 'Reg)
        'Nest
        '[ 'DdK ('SelSymbol "id") '[] i 'Prim,
           'DdK
             'SelAuto
             '[]
             (Queued t d)
             ('Comp
                ('TSel 'NoPrefix (AppendSymbol "Queued" name))
                ('Prod 'Reg)
                'Merge
                '[ 'DdK ('SelSymbol "queue_created") '[] t 'Prim,
                   'DdK
                     ('SelSymbol "queue_payload")
                     '[PgPrimName, PrimValueCodec d]
                     d
                     'Prim])]))
table = forall i a (si :: DdK) (sa :: DdK) (s :: DdK).
UidColumn i a si sa s =>
Dd si -> Dd sa -> Dd s
uid forall a. Dd ('DdK 'SelAuto '[] a 'Prim)
prim (forall a arg (s :: DdK). Product a arg s => arg -> Dd s
prod (forall a. Dd ('DdK 'SelAuto '[] a 'Prim)
prim forall {k} (a :: k -> *) (x :: k) (xs :: [k]).
a x -> NP a xs -> NP a (x : xs)
:* forall a.
(ToJSON a, FromJSON a) =>
Dd ('DdK 'SelAuto '[PgPrimName, PrimValueCodec a] a 'Prim)
Sqel.json forall {k} (a :: k -> *) (x :: k) (xs :: [k]).
a x -> NP a xs -> NP a (x : xs)
:* forall {k} (a :: k -> *). NP a '[]
Nil))

interpretQueueStoreDb ::
   d t dt r .
  StoreTable t d =>
  Members [Database !! DbError, Time t dt, Log, Resource, Async, Race, Embed IO, Final IO] r =>
  InterpreterFor (Store UUID (Queued t d) !! DbError) r
interpretQueueStoreDb :: forall d t dt (r :: EffectRow).
(StoreTable t d,
 Members
   '[Database !! DbError, Time t dt, Log, Resource, Async, Race,
     Embed IO, Final IO]
   r) =>
InterpreterFor (Store UUID (Queued t d) !! DbError) r
interpretQueueStoreDb =
  forall d (r :: EffectRow).
Members '[Database !! DbError, Log, Embed IO] r =>
TableSchema d -> InterpreterFor (DbTable d !! DbError) r
interpretTable TableSchema (Uuid (Queued t d))
ts forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall i d e (r :: EffectRow).
Member (StoreTable i d !! e) r =>
TableSchema (Uid i d)
-> QuerySchema i (Uid i d) -> InterpreterFor (Store i d !! e) r
interpretStoreDb TableSchema (Uuid (Queued t d))
ts QuerySchema UUID (Uuid (Queued t d))
qs forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall (e2 :: (* -> *) -> * -> *) (e1 :: (* -> *) -> * -> *)
       (r :: EffectRow) a.
Sem (e1 : r) a -> Sem (e1 : e2 : r) a
raiseUnder
  where
    (TableSchema (Uuid (Queued t d))
ts, QuerySchema UUID (Uuid (Queued t d))
qs) = forall t d.
StoreTable t d =>
(TableSchema (Uuid (Queued t d)),
 QuerySchema UUID (Uuid (Queued t d)))
storeTable @t