{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Periodic.Server
  ( startServer
  ) where


import           Metro                     (NodeMode (..), SessionMode (..))
import           Metro.Class               (Servable (STP),
                                            Transport (TransportConfig))
import qualified Metro.Class               as S (Servable (ServerConfig))
import           Metro.Conn                (receive, runConnT, send)
import           Metro.Server              (initServerEnv, runServerT,
                                            setDefaultSessionTimeout,
                                            setKeepalive, setNodeMode,
                                            setOnNodeLeave, setServerName,
                                            setSessionMode, stopServerT)
import qualified Metro.Server              as M (ServerEnv, startServer)
import           Periodic.IOList           (newIOList, toList)
import           Periodic.Node             (sessionGen)
import           Periodic.Server.Client    (handleSessionT)
import           Periodic.Server.Persist   (Persist, PersistConfig)
import           Periodic.Server.Scheduler (failJob, initSchedEnv, removeFunc,
                                            runSchedT, shutdown, startSchedT)
import           Periodic.Server.Types     (ClientConfig (..), Command,
                                            ServerCommand (Data))
import           Periodic.Types            (ClientType, Msgid, Nid (..), Packet,
                                            getClientType, regPacketRES)
import           System.Entropy            (getEntropy)
import           UnliftIO                  (MonadUnliftIO)

type ServerEnv serv =
  M.ServerEnv serv ClientConfig Nid Msgid (Packet Command)

startServer
  :: (Servable serv, Transport tp, Persist db, MonadUnliftIO m)
  => PersistConfig db
  -> (TransportConfig (STP serv) -> TransportConfig tp)
  -> S.ServerConfig serv
  -> m ()
startServer dbconfig mk config = do
  sEnv <- fmap mapEnv . initServerEnv config sessionGen mk $ \_ connEnv -> do
    (_ :: ClientType) <- getClientType <$> runConnT connEnv receive
    nid <- getEntropy 4
    runConnT connEnv $ send (regPacketRES $ Data nid)
    wFuncList <- newIOList
    wJobQueue <- newIOList
    return $ Just (Nid nid, ClientConfig {..})

  schedEnv <- initSchedEnv dbconfig $ runServerT sEnv stopServerT

  setOnNodeLeave sEnv $ \_ ClientConfig {..} ->
    runSchedT schedEnv $ do
      mapM_ failJob =<< toList wJobQueue
      mapM_ removeFunc =<< toList wFuncList

  runSchedT schedEnv $ do
    startSchedT
    M.startServer sEnv handleSessionT
    shutdown
  where mapEnv :: ServerEnv serv tp -> ServerEnv serv tp
        mapEnv =
          setNodeMode Multi
          . setSessionMode SingleAction
          . setKeepalive 500
          . setDefaultSessionTimeout 100
          . setServerName "Periodic"