{-# LANGUAGE TypeApplications #-}

module BtcLsp.Storage.Model.User
  ( createVerifySql,
  )
where

import BtcLsp.Data.Orphan ()
import BtcLsp.Import hiding (Storage (..))
import qualified BtcLsp.Import.Psql as Psql
import qualified BtcLsp.Storage.Util as Util

--
-- NOTE : We will not create withVerifiedNonce
-- for now to reduce complexity overall.
-- Plus this combinator enables all kinds of
-- possibilities for deadlocks.
--
createVerifySql ::
  ( MonadIO m
  ) =>
  NodePubKey ->
  Nonce ->
  ReaderT Psql.SqlBackend m (Either Failure (Entity User))
createVerifySql :: forall (m :: * -> *).
MonadIO m =>
NodePubKey
-> Nonce -> ReaderT SqlBackend m (Either Failure (Entity User))
createVerifySql NodePubKey
pub Nonce
nonce = do
  UTCTime
ct <- ReaderT SqlBackend m UTCTime
forall (m :: * -> *). MonadIO m => m UTCTime
getCurrentTime
  let zeroRow :: User
zeroRow =
        User :: NodePubKey -> Nonce -> UTCTime -> UTCTime -> User
User
          { userNodePubKey :: NodePubKey
userNodePubKey = NodePubKey
pub,
            userLatestNonce :: Nonce
userLatestNonce = forall source target.
(From source target, 'False ~ (source == target)) =>
source -> target
from @Word64 Word64
0,
            userInsertedAt :: UTCTime
userInsertedAt = UTCTime
ct,
            userUpdatedAt :: UTCTime
userUpdatedAt = UTCTime
ct
          }
  Key User
rowId <-
    Entity User -> Key User
forall record. Entity record -> Key record
entityKey
      (Entity User -> Key User)
-> ReaderT SqlBackend m (Entity User)
-> ReaderT SqlBackend m (Key User)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Unique User
-> User
-> [SqlExpr (Entity User) -> SqlExpr Update]
-> ReaderT SqlBackend m (Entity User)
forall (m :: * -> *) record.
(MonadIO m, PersistEntity record,
 IsPersistBackend (PersistEntityBackend record)) =>
Unique record
-> record
-> [SqlExpr (Entity record) -> SqlExpr Update]
-> ReaderT SqlBackend m (Entity record)
Psql.upsertBy
        (NodePubKey -> Unique User
UniqueUser NodePubKey
pub)
        User
zeroRow
        [ EntityField User UTCTime
forall typ. (typ ~ UTCTime) => EntityField User typ
UserUpdatedAt EntityField User UTCTime
-> SqlExpr (Value UTCTime)
-> SqlExpr (Entity User)
-> SqlExpr Update
forall val typ.
(PersistEntity val, PersistField typ) =>
EntityField val typ
-> SqlExpr (Value typ) -> SqlExpr (Entity val) -> SqlExpr Update
Psql.=. UTCTime -> SqlExpr (Value UTCTime)
forall typ. PersistField typ => typ -> SqlExpr (Value typ)
Psql.val UTCTime
ct
        ]
  User
existingRow <- Key User -> SqlPersistT m User
forall (m :: * -> *) a.
(MonadIO m, HasTable a, ToBackendKey SqlBackend a) =>
Key a -> SqlPersistT m a
Util.lockByRow Key User
rowId
  if (User
existingRow User -> User -> Bool
forall a. Eq a => a -> a -> Bool
== User
zeroRow)
    Bool -> Bool -> Bool
|| (User -> Nonce
userLatestNonce User
existingRow Nonce -> Nonce -> Bool
forall a. Ord a => a -> a -> Bool
< Nonce
nonce)
    then
      Entity User -> Either Failure (Entity User)
forall a b. b -> Either a b
Right
        (Entity User -> Either Failure (Entity User))
-> ReaderT SqlBackend m (Entity User)
-> ReaderT SqlBackend m (Either Failure (Entity User))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Unique User
-> User
-> [SqlExpr (Entity User) -> SqlExpr Update]
-> ReaderT SqlBackend m (Entity User)
forall (m :: * -> *) record.
(MonadIO m, PersistEntity record,
 IsPersistBackend (PersistEntityBackend record)) =>
Unique record
-> record
-> [SqlExpr (Entity record) -> SqlExpr Update]
-> ReaderT SqlBackend m (Entity record)
Psql.upsertBy
          (NodePubKey -> Unique User
UniqueUser NodePubKey
pub)
          User
zeroRow
            { userLatestNonce :: Nonce
userLatestNonce = Nonce
nonce
            }
          [ EntityField User Nonce
forall typ. (typ ~ Nonce) => EntityField User typ
UserLatestNonce EntityField User Nonce
-> SqlExpr (Value Nonce) -> SqlExpr (Entity User) -> SqlExpr Update
forall val typ.
(PersistEntity val, PersistField typ) =>
EntityField val typ
-> SqlExpr (Value typ) -> SqlExpr (Entity val) -> SqlExpr Update
Psql.=. Nonce -> SqlExpr (Value Nonce)
forall typ. PersistField typ => typ -> SqlExpr (Value typ)
Psql.val Nonce
nonce,
            EntityField User UTCTime
forall typ. (typ ~ UTCTime) => EntityField User typ
UserUpdatedAt EntityField User UTCTime
-> SqlExpr (Value UTCTime)
-> SqlExpr (Entity User)
-> SqlExpr Update
forall val typ.
(PersistEntity val, PersistField typ) =>
EntityField val typ
-> SqlExpr (Value typ) -> SqlExpr (Entity val) -> SqlExpr Update
Psql.=. UTCTime -> SqlExpr (Value UTCTime)
forall typ. PersistField typ => typ -> SqlExpr (Value typ)
Psql.val UTCTime
ct
          ]
    else
      Either Failure (Entity User)
-> ReaderT SqlBackend m (Either Failure (Entity User))
forall (f :: * -> *) a. Applicative f => a -> f a
pure
        (Either Failure (Entity User)
 -> ReaderT SqlBackend m (Either Failure (Entity User)))
-> (Failure -> Either Failure (Entity User))
-> Failure
-> ReaderT SqlBackend m (Either Failure (Entity User))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Failure -> Either Failure (Entity User)
forall a b. a -> Either a b
Left
        (Failure -> ReaderT SqlBackend m (Either Failure (Entity User)))
-> Failure -> ReaderT SqlBackend m (Either Failure (Entity User))
forall a b. (a -> b) -> a -> b
$ FailureInput -> Failure
FailureInp FailureInput
FailureNonce