module IHP.DataSync.Types where

import IHP.Prelude
import Data.Aeson
import IHP.QueryBuilder
import IHP.DataSync.DynamicQuery
import Data.HashMap.Strict (HashMap)
import qualified IHP.PGListener as PGListener
import qualified Database.PostgreSQL.Simple as PG
import Control.Concurrent.MVar as MVar
import qualified IHP.GraphQL.Types as GraphQL


data DataSyncMessage
    = DataSyncQuery { DataSyncMessage -> DynamicSQLQuery
query :: !DynamicSQLQuery, DataSyncMessage -> Int
requestId :: !Int, DataSyncMessage -> Maybe UUID
transactionId :: !(Maybe UUID) }
    | CreateDataSubscription { query :: !DynamicSQLQuery, requestId :: !Int }
    | DeleteDataSubscription { DataSyncMessage -> UUID
subscriptionId :: !UUID, requestId :: !Int }
    | CreateRecordMessage { DataSyncMessage -> Text
table :: !Text, DataSyncMessage -> HashMap Text Value
record :: !(HashMap Text Value), requestId :: !Int, transactionId :: !(Maybe UUID) }
    | CreateRecordsMessage { table :: !Text, DataSyncMessage -> [HashMap Text Value]
records :: ![HashMap Text Value], requestId :: !Int, transactionId :: !(Maybe UUID) }
    | UpdateRecordMessage { table :: !Text, DataSyncMessage -> UUID
id :: !UUID, DataSyncMessage -> HashMap Text Value
patch :: !(HashMap Text Value), requestId :: !Int, transactionId :: !(Maybe UUID) }
    | UpdateRecordsMessage { table :: !Text, DataSyncMessage -> [UUID]
ids :: ![UUID], patch :: !(HashMap Text Value), requestId :: !Int, transactionId :: !(Maybe UUID) }
    | DeleteRecordMessage { table :: !Text, id :: !UUID, requestId :: !Int, transactionId :: !(Maybe UUID) }
    | DeleteRecordsMessage { table :: !Text, ids :: ![UUID], requestId :: !Int, transactionId :: !(Maybe UUID) }
    | StartTransaction { requestId :: !Int }
    | RollbackTransaction { requestId :: !Int, id :: !UUID }
    | CommitTransaction { requestId :: !Int, id :: !UUID }
    | LoginWithEmailAndPassword { requestId :: !Int, DataSyncMessage -> Text
email :: !Text, DataSyncMessage -> Text
password :: !Text }
    | LoginWithJWT { requestId :: !Int, DataSyncMessage -> Text
jwt :: !Text }
    | CreateUser { requestId :: !Int, email :: !Text, password :: !Text }
    | ConfirmUser { requestId :: !Int, DataSyncMessage -> UUID
userId :: !UUID, DataSyncMessage -> Text
token :: !Text }
    deriving (DataSyncMessage -> DataSyncMessage -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DataSyncMessage -> DataSyncMessage -> Bool
$c/= :: DataSyncMessage -> DataSyncMessage -> Bool
== :: DataSyncMessage -> DataSyncMessage -> Bool
$c== :: DataSyncMessage -> DataSyncMessage -> Bool
Eq, Int -> DataSyncMessage -> ShowS
[DataSyncMessage] -> ShowS
DataSyncMessage -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DataSyncMessage] -> ShowS
$cshowList :: [DataSyncMessage] -> ShowS
show :: DataSyncMessage -> String
$cshow :: DataSyncMessage -> String
showsPrec :: Int -> DataSyncMessage -> ShowS
$cshowsPrec :: Int -> DataSyncMessage -> ShowS
Show)

data DataSyncResponse
    = DataSyncResult { DataSyncResponse -> [[Field]]
result :: ![[Field]], DataSyncResponse -> Int
requestId :: !Int }
    | DataSyncError { requestId :: !Int, DataSyncResponse -> Text
errorMessage :: !Text }
    | FailedToDecodeMessageError { errorMessage :: !Text }
    | DidCreateDataSubscription { requestId :: !Int, DataSyncResponse -> UUID
subscriptionId :: !UUID, result :: ![[Field]] }
    | DidDeleteDataSubscription { requestId :: !Int, subscriptionId :: !UUID }
    | DidInsert { subscriptionId :: !UUID, DataSyncResponse -> [Field]
record :: ![Field] }
    | DidUpdate { subscriptionId :: !UUID, DataSyncResponse -> UUID
id :: UUID, DataSyncResponse -> Value
changeSet :: !Value }
    | DidDelete { subscriptionId :: !UUID, id :: !UUID }
    | DidCreateRecord { requestId :: !Int, record :: ![Field] } -- ^ Response to 'CreateRecordMessage'
    | DidCreateRecords { requestId :: !Int, DataSyncResponse -> [[Field]]
records :: ![[Field]] } -- ^ Response to 'CreateRecordsMessage'
    | DidUpdateRecord { requestId :: !Int, record :: ![Field] } -- ^ Response to 'UpdateRecordMessage'
    | DidUpdateRecords { requestId :: !Int, records :: ![[Field]] } -- ^ Response to 'UpdateRecordsMessage'
    | DidDeleteRecord { requestId :: !Int }
    | DidDeleteRecords { requestId :: !Int }
    | DidStartTransaction { requestId :: !Int, DataSyncResponse -> UUID
transactionId :: !UUID }
    | DidRollbackTransaction { requestId :: !Int, transactionId :: !UUID }
    | DidCommitTransaction { requestId :: !Int, transactionId :: !UUID }

    | LoginSuccessful { requestId :: !Int, DataSyncResponse -> UUID
userId :: !UUID, DataSyncResponse -> Text
jwt :: !Text }
    | UserLocked { requestId :: !Int }
    | UserUnconfirmed { requestId :: !Int }
    | InvalidCredentials { requestId :: !Int }

    | DidCreateUser { requestId :: !Int, userId :: !UUID, DataSyncResponse -> Bool
emailConfirmationRequired :: !Bool, jwt :: !Text }
    | CreateUserFailed { requestId :: !Int, DataSyncResponse -> [(Text, Text)]
validationFailures :: [(Text, Text)] }
    | DidConfirmUser { requestId :: !Int, jwt :: !Text }
    | DidConfirmUserAlready { requestId :: !Int }
    | ConfirmUserFailed { requestId :: !Int }

data GraphQLResult = GraphQLResult { GraphQLResult -> UndecodedJSON
graphQLResult :: !UndecodedJSON, GraphQLResult -> Int
requestId :: !Int }

data DataSyncTransaction
    = DataSyncTransaction
    { DataSyncTransaction -> UUID
id :: !UUID
    , DataSyncTransaction -> Connection
connection :: !PG.Connection
    , DataSyncTransaction -> MVar ()
close :: MVar ()
    }

data DataSyncController
    = DataSyncController
    | DataSyncReady
        { DataSyncController -> HashMap UUID (MVar ())
subscriptions :: !(HashMap UUID (MVar.MVar ()))
        , DataSyncController -> HashMap UUID DataSyncTransaction
transactions :: !(HashMap UUID DataSyncTransaction)
        , DataSyncController -> [Async ()]
asyncs :: ![Async ()]
        }