{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}

module Simplex.Messaging.Agent.Env.SQLite where

import Control.Monad.IO.Unlift
import Crypto.Random
import Data.List.NonEmpty (NonEmpty)
import Network.Socket
import Numeric.Natural
import Simplex.Messaging.Agent.Protocol (SMPServer)
import Simplex.Messaging.Agent.Store.SQLite
import Simplex.Messaging.Client
import System.Random (StdGen, newStdGen)
import UnliftIO.STM

data AgentConfig = AgentConfig
  { AgentConfig -> ServiceName
tcpPort :: ServiceName,
    AgentConfig -> NonEmpty SMPServer
smpServers :: NonEmpty SMPServer,
    AgentConfig -> Int
rsaKeySize :: Int,
    AgentConfig -> Int
connIdBytes :: Int,
    AgentConfig -> Natural
tbqSize :: Natural,
    AgentConfig -> ServiceName
dbFile :: FilePath,
    AgentConfig -> SMPClientConfig
smpCfg :: SMPClientConfig
  }

data Env = Env
  { Env -> AgentConfig
config :: AgentConfig,
    Env -> TVar ChaChaDRG
idsDrg :: TVar ChaChaDRG,
    Env -> TVar Int
clientCounter :: TVar Int,
    Env -> Int
reservedMsgSize :: Int,
    Env -> TVar StdGen
randomServer :: TVar StdGen
  }

newSMPAgentEnv :: (MonadUnliftIO m, MonadRandom m) => AgentConfig -> m Env
newSMPAgentEnv :: AgentConfig -> m Env
newSMPAgentEnv AgentConfig
config = do
  TVar ChaChaDRG
idsDrg <- ChaChaDRG -> m (TVar ChaChaDRG)
forall (m :: * -> *) a. MonadIO m => a -> m (TVar a)
newTVarIO (ChaChaDRG -> m (TVar ChaChaDRG))
-> m ChaChaDRG -> m (TVar ChaChaDRG)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< m ChaChaDRG
forall (randomly :: * -> *).
MonadRandom randomly =>
randomly ChaChaDRG
drgNew
  SQLiteStore
_ <- ServiceName -> m SQLiteStore
forall (m :: * -> *).
MonadUnliftIO m =>
ServiceName -> m SQLiteStore
createSQLiteStore (ServiceName -> m SQLiteStore) -> ServiceName -> m SQLiteStore
forall a b. (a -> b) -> a -> b
$ AgentConfig -> ServiceName
dbFile AgentConfig
config
  TVar Int
clientCounter <- Int -> m (TVar Int)
forall (m :: * -> *) a. MonadIO m => a -> m (TVar a)
newTVarIO Int
0
  TVar StdGen
randomServer <- StdGen -> m (TVar StdGen)
forall (m :: * -> *) a. MonadIO m => a -> m (TVar a)
newTVarIO (StdGen -> m (TVar StdGen)) -> m StdGen -> m (TVar StdGen)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO StdGen -> m StdGen
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO StdGen
newStdGen
  Env -> m Env
forall (m :: * -> *) a. Monad m => a -> m a
return Env :: AgentConfig
-> TVar ChaChaDRG -> TVar Int -> Int -> TVar StdGen -> Env
Env {AgentConfig
config :: AgentConfig
$sel:config:Env :: AgentConfig
config, TVar ChaChaDRG
idsDrg :: TVar ChaChaDRG
$sel:idsDrg:Env :: TVar ChaChaDRG
idsDrg, TVar Int
clientCounter :: TVar Int
$sel:clientCounter:Env :: TVar Int
clientCounter, Int
reservedMsgSize :: Int
$sel:reservedMsgSize:Env :: Int
reservedMsgSize, TVar StdGen
randomServer :: TVar StdGen
$sel:randomServer:Env :: TVar StdGen
randomServer}
  where
    -- 1st rsaKeySize is used by the RSA signature in each command,
    -- 2nd - by encrypted message body header
    -- 3rd - by message signature
    -- smpCommandSize - is the estimated max size for SMP command, queueId, corrId
    reservedMsgSize :: Int
reservedMsgSize = Int
3 Int -> Int -> Int
forall a. Num a => a -> a -> a
* AgentConfig -> Int
rsaKeySize AgentConfig
config Int -> Int -> Int
forall a. Num a => a -> a -> a
+ SMPClientConfig -> Int
smpCommandSize (AgentConfig -> SMPClientConfig
smpCfg AgentConfig
config)