-- | Description: Interpreters for 'Accounts' and 'Password' using PostgreSQL backends
module Polysemy.Account.Api.Interpreter.Accounts where

import Data.UUID (UUID)
import Polysemy.Db (DbError, Id, InitDbError, Store)
import Polysemy.Hasql (Database, StoreTable, interpretStoreDb, interpretTable)
import Sqel (CheckedProjection, Dd, FullCodec, primIdQuery)
import Sqel.Codec (PrimColumn)
import Sqel.Ext (Column, ReifyCodec, ReifyDd)
import Sqel.Query (checkQuery)

import Polysemy.Account.Data.Account (Account)
import Polysemy.Account.Data.AccountAuth (AccountAuth)
import Polysemy.Account.Data.AccountsConfig (AccountsConfig)
import Polysemy.Account.Data.AccountsError (AccountsError)
import qualified Polysemy.Account.Db.Dd as Dd
import Polysemy.Account.Db.Dd (DdAccount)
import Polysemy.Account.Db.Interpreter.AccountByName (interpretQueryAccountByNameDb)
import Polysemy.Account.Db.Interpreter.AuthForAccount (interpretQueryAuthForAccountDb)
import Polysemy.Account.Effect.Accounts (Accounts)
import Polysemy.Account.Effect.Password (Password)
import Polysemy.Account.Interpreter.Accounts (interpretAccounts)
import Polysemy.Account.Interpreter.Password (interpretPassword)

-- | Interpret 'Polysemy.Hasql.DbTable' for 'Account'.
interpretAccountTable ::
   i p s r .
  PrimColumn i =>
  Column p "privileges" s s =>
  ReifyCodec FullCodec s p =>
  ReifyDd s =>
  Dd s ->
  Members [Database !! DbError, Log, Embed IO] r =>
  InterpreterFor (StoreTable i (Account p) !! DbError) r
interpretAccountTable :: forall i p (s :: DdK) (r :: EffectRow).
(PrimColumn i, Column p "privileges" s s, ReifyCodec FullCodec s p,
 ReifyDd s) =>
Dd s
-> Members '[Database !! DbError, Log, Embed IO] r =>
   InterpreterFor (StoreTable i (Account p) !! DbError) r
interpretAccountTable Dd s
priv =
  forall d (r :: EffectRow).
Members '[Database !! DbError, Log, Embed IO] r =>
TableSchema d -> InterpreterFor (DbTable d !! DbError) r
interpretTable (forall i p (s :: DdK).
(PrimColumn i, Column p "privileges" s s, ReifyCodec FullCodec s p,
 ReifyDd s) =>
Dd s -> TableSchema (Uid i (Account p))
Dd.accountSchema Dd s
priv)

-- | Interpret 'Polysemy.Hasql.DbTable' for 'AccountAuth'.
interpretAccountAuthTable ::
   i r .
  PrimColumn i =>
  Members [Database !! DbError, Log, Embed IO] r =>
  InterpreterFor (StoreTable i (AccountAuth i) !! DbError) r
interpretAccountAuthTable :: forall i (r :: EffectRow).
(PrimColumn i, Members '[Database !! DbError, Log, Embed IO] r) =>
InterpreterFor (StoreTable i (AccountAuth i) !! DbError) r
interpretAccountAuthTable =
  forall d (r :: EffectRow).
Members '[Database !! DbError, Log, Embed IO] r =>
TableSchema d -> InterpreterFor (DbTable d !! DbError) r
interpretTable forall i. PrimColumn i => TableSchema (Uid i (AccountAuth i))
Dd.accountAuthSchema

-- | Interpret 'Store' for 'Account' as a 'Polysemy.Hasql.DbTable'.
interpretAccountStore ::
   i p s r .
  PrimColumn i =>
  Column p "privileges" s s =>
  ReifyCodec FullCodec s p =>
  ReifyDd s =>
  Dd s ->
  Member (StoreTable i (Account p) !! DbError) r =>
  InterpreterFor (Store i (Account p) !! DbError) r
interpretAccountStore :: forall i p (s :: DdK) (r :: EffectRow).
(PrimColumn i, Column p "privileges" s s, ReifyCodec FullCodec s p,
 ReifyDd s) =>
Dd s
-> Member (StoreTable i (Account p) !! DbError) r =>
   InterpreterFor (Store i (Account p) !! DbError) r
interpretAccountStore Dd s
priv =
  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 (forall i p (s :: DdK).
(PrimColumn i, Column p "privileges" s s, ReifyCodec FullCodec s p,
 ReifyDd s) =>
Dd s -> TableSchema (Uid i (Account p))
Dd.accountSchema Dd s
priv) (forall (query :: DdK) (table :: DdK).
CheckQuery query table =>
Dd query -> Dd table -> QuerySchema (DdType query) (DdType table)
checkQuery forall a. Dd ('DdK ('SelSymbol "id") NoMods a 'Prim)
primIdQuery (forall p (s :: DdK) i.
Column p "privileges" s s =>
Dd s -> Dd (DdAccount i p s)
Dd.account Dd s
priv))

-- | Interpret 'Store' for 'AccountAuth' as a 'Polysemy.Hasql.DbTable'.
interpretAccountAuthStore ::
   i r .
  PrimColumn i =>
  Member (StoreTable i (AccountAuth i) !! DbError) r =>
  InterpreterFor (Store i (AccountAuth i) !! DbError) r
interpretAccountAuthStore :: forall i (r :: EffectRow).
(PrimColumn i,
 Member (StoreTable i (AccountAuth i) !! DbError) r) =>
InterpreterFor (Store i (AccountAuth i) !! DbError) r
interpretAccountAuthStore =
  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 forall i. PrimColumn i => TableSchema (Uid i (AccountAuth i))
Dd.accountAuthSchema (forall (query :: DdK) (table :: DdK).
CheckQuery query table =>
Dd query -> Dd table -> QuerySchema (DdType query) (DdType table)
checkQuery forall a. Dd ('DdK ('SelSymbol "id") NoMods a 'Prim)
primIdQuery forall i.
Dd
  ('DdK
     'SelAuto
     NoMods
     (Uid i (AccountAuth i))
     ('Comp
        ('TSel 'DefaultPrefix "AccountAuth")
        ('Prod 'Reg)
        'Nest
        '[ 'DdK ('SelSymbol "id") (PrimaryKey : NoMods) i 'Prim,
           'DdK
             'SelAuto
             NoMods
             (AccountAuth i)
             ('Comp
                ('TSel 'DefaultPrefix "AccountAuth")
                ('Prod 'Reg)
                'Merge
                '[ 'DdK ('SelSymbol "account") NoMods i 'Prim,
                   'DdK
                     ('SelSymbol "description")
                     (Newtype AccountAuthDescription Text : NoMods)
                     AccountAuthDescription
                     'Prim,
                   'DdK
                     ('SelSymbol "password")
                     (Newtype HashedPassword Text : NoMods)
                     HashedPassword
                     'Prim,
                   'DdK
                     ('SelSymbol "expiry")
                     (Nullable : NoMods)
                     (Maybe Datetime)
                     'Prim])]))
Dd.accountAuth)

-- | Interpret 'Accounts' and 'Password' using PostgreSQL as storage backend.
interpretAccountsDb ::
   p s r .
  Members [Database !! DbError, Id UUID, Log, Error InitDbError, Embed IO] r =>
  Column p "privileges" s s =>
  ReifyCodec FullCodec s p =>
  ReifyDd s =>
  CheckedProjection (DdAccount UUID p s) (DdAccount UUID p s) =>
  Dd s ->
  AccountsConfig p ->
  InterpretersFor [Accounts UUID p !! AccountsError, Password] r
interpretAccountsDb :: forall p (s :: DdK) (r :: EffectRow).
(Members
   '[Database !! DbError, Id UUID, Log, Error InitDbError, Embed IO]
   r,
 Column p "privileges" s s, ReifyCodec FullCodec s p, ReifyDd s,
 CheckedProjection (DdAccount UUID p s) (DdAccount UUID p s)) =>
Dd s
-> AccountsConfig p
-> InterpretersFor '[Accounts UUID p !! AccountsError, Password] r
interpretAccountsDb Dd s
priv AccountsConfig p
conf =
  forall (r :: EffectRow).
Member (Embed IO) r =>
InterpreterFor Password r
interpretPassword forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall err (eff :: (* -> *) -> * -> *) (r :: EffectRow).
InterpreterTrans (Resumable err eff) eff r
raiseResumable (forall i (r :: EffectRow) a. i -> Sem (Reader i : r) a -> Sem r a
runReader AccountsConfig p
conf) forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall i p (s :: DdK) (r :: EffectRow).
(PrimColumn i, Column p "privileges" s s, ReifyCodec FullCodec s p,
 ReifyDd s) =>
Dd s
-> Members '[Database !! DbError, Log, Embed IO] r =>
   InterpreterFor (StoreTable i (Account p) !! DbError) r
interpretAccountTable Dd s
priv forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall p (s :: DdK) (r :: EffectRow).
(Column p "privileges" s s, ReifyCodec FullCodec s p, ReifyDd s,
 CheckedProjection (DdAccount UUID p s) (DdAccount UUID p s),
 Members
   '[DbTable (Uuid (Account p)) !! DbError, Error InitDbError] r) =>
Dd s
-> InterpreterFor
     (Query AccountByName (Maybe (Uuid (Account p))) !! DbError) r
interpretQueryAccountByNameDb Dd s
priv forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall i (r :: EffectRow).
(PrimColumn i, Members '[Database !! DbError, Log, Embed IO] r) =>
InterpreterFor (StoreTable i (AccountAuth i) !! DbError) r
interpretAccountAuthTable forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall (r :: EffectRow).
Member (DbTable (Uuid (AccountAuth UUID)) !! DbError) r =>
InterpreterFor
  (Query (AuthForAccount UUID) [Uuid (AccountAuth UUID)] !! DbError)
  r
interpretQueryAuthForAccountDb forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall i p (s :: DdK) (r :: EffectRow).
(PrimColumn i, Column p "privileges" s s, ReifyCodec FullCodec s p,
 ReifyDd s) =>
Dd s
-> Member (StoreTable i (Account p) !! DbError) r =>
   InterpreterFor (Store i (Account p) !! DbError) r
interpretAccountStore Dd s
priv forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall i (r :: EffectRow).
(PrimColumn i,
 Member (StoreTable i (AccountAuth i) !! DbError) r) =>
InterpreterFor (Store i (AccountAuth i) !! DbError) r
interpretAccountAuthStore forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall e i p (r :: EffectRow).
(Show e,
 Member (Query AccountByName (Maybe (Uid i (Account p))) !! e) r,
 Member (Query (AuthForAccount i) [Uid i (AccountAuth i)] !! e) r,
 Members
   '[Password, Store i (Account p) !! e, Store i (AccountAuth i) !! e,
     Reader (AccountsConfig p) !! e, Id i]
   r) =>
InterpreterFor (Accounts i p !! AccountsError) r
interpretAccounts forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  forall (index :: Nat) (inserted :: EffectRow) (head :: EffectRow)
       (oldTail :: EffectRow) (tail :: EffectRow) (old :: EffectRow)
       (full :: EffectRow) a.
(ListOfLength index head, WhenStuck index InsertAtUnprovidedIndex,
 old ~ Append head oldTail, tail ~ Append inserted oldTail,
 full ~ Append head tail,
 InsertAtIndex index head tail oldTail full inserted) =>
Sem old a -> Sem full a
insertAt @1