module Web.Spock.Internal.Types where
import Web.Spock.Internal.Wire
#if MIN_VERSION_base(4,8,0)
#else
import Control.Applicative
#endif
import Control.Concurrent.STM
import Control.Monad.Base
import Control.Monad.Reader
import Control.Monad.Trans.Control
import Control.Monad.Trans.Resource
import Data.Hashable
import Data.Pool
import Data.Time.Clock ( UTCTime(..), NominalDiffTime )
import Data.Typeable
import Data.Word
import Network.Wai
import qualified Data.HashMap.Strict as HM
import qualified Data.Text as T
type SpockAllM r conn sess st a =
    SpockAllT r (WebStateM conn sess st) a
type SpockActionCtx ctx conn sess st = ActionCtxT ctx (WebStateM conn sess st)
type SpockAction conn sess st = SpockActionCtx () conn sess st
data SpockCfg conn sess st
   = SpockCfg
   { spc_initialState :: st
     
   , spc_database :: PoolOrConn conn
     
   , spc_sessionCfg :: SessionCfg sess
     
   , spc_maxRequestSize :: Maybe Word64
     
   }
defaultSpockCfg :: sess -> PoolOrConn conn -> st -> SpockCfg conn sess st
defaultSpockCfg sess conn st =
    SpockCfg
    { spc_initialState = st
    , spc_database = conn
    , spc_sessionCfg = defaultSessionCfg sess
    , spc_maxRequestSize = Just (5 * 1024 * 1024)
    }
data PoolCfg
   = PoolCfg
   { pc_stripes :: Int
   , pc_resPerStripe :: Int
   , pc_keepOpenTime :: NominalDiffTime
   }
data ConnBuilder a
   = ConnBuilder
   { cb_createConn :: IO a
   , cb_destroyConn :: a -> IO ()
   , cb_poolConfiguration :: PoolCfg
   }
data PoolOrConn a where
    PCPool :: Pool a -> PoolOrConn a
    PCConn :: ConnBuilder a -> PoolOrConn a
    PCNoDatabase :: PoolOrConn ()
defaultSessionCfg :: a -> SessionCfg a
defaultSessionCfg emptySession =
    SessionCfg
    { sc_cookieName = "spockcookie"
    , sc_sessionTTL = 3600
    , sc_sessionIdEntropy = 64
    , sc_sessionExpandTTL = True
    , sc_emptySession = emptySession
    , sc_persistCfg = Nothing
    , sc_housekeepingInterval = 60 * 10
    , sc_hooks = defaultSessionHooks
    }
data SessionCfg a
   = SessionCfg
   { sc_cookieName :: T.Text
     
   , sc_sessionTTL :: NominalDiffTime
     
   , sc_sessionIdEntropy :: Int
     
   , sc_sessionExpandTTL :: Bool
     
   , sc_emptySession :: a
     
   , sc_persistCfg :: Maybe (SessionPersistCfg a)
     
   , sc_housekeepingInterval :: NominalDiffTime
     
   , sc_hooks :: SessionHooks a
     
   }
defaultSessionHooks :: SessionHooks a
defaultSessionHooks =
    SessionHooks
    { sh_removed = const $ return ()
    }
data SessionHooks a
   = SessionHooks
   { sh_removed :: HM.HashMap SessionId a -> IO ()
   }
data SessionPersistCfg a
   = SessionPersistCfg
   { spc_load :: IO [(SessionId, UTCTime, a)]
   , spc_store :: [(SessionId, UTCTime, a)] -> IO ()
   }
data WebState conn sess st
   = WebState
   { web_dbConn :: Pool conn
   , web_sessionMgr :: SessionManager conn sess st
   , web_state :: st
   }
class HasSpock m where
    type SpockConn m :: *
    type SpockState m :: *
    type SpockSession m :: *
    
    
    runQuery :: (SpockConn m -> IO a) -> m a
    
    
    getState :: m (SpockState m)
    
    getSessMgr :: m (SessionManager (SpockConn m) (SpockSession m) (SpockState m))
class (Hashable a, Eq a, Typeable a) => SafeAction conn sess st a where
    
    runSafeAction :: a -> SpockAction conn sess st ()
data PackedSafeAction conn sess st
    = forall a. (SafeAction conn sess st a) => PackedSafeAction { unpackSafeAction :: a }
instance Hashable (PackedSafeAction conn sess st) where
    hashWithSalt i (PackedSafeAction a) = hashWithSalt i a
instance Eq (PackedSafeAction conn sess st) where
   (PackedSafeAction a) == (PackedSafeAction b) =
       cast a == Just b
data SafeActionStore conn sess st
   = SafeActionStore
   { sas_forward :: !(HM.HashMap SafeActionHash (PackedSafeAction conn sess st))
   , sas_reverse :: !(HM.HashMap (PackedSafeAction conn sess st) SafeActionHash)
   }
type SafeActionHash = T.Text
newtype WebStateT conn sess st m a = WebStateT { runWebStateT :: ReaderT (WebState conn sess st) m a }
    deriving (Monad, Functor, Applicative, MonadIO, MonadReader (WebState conn sess st), MonadTrans)
instance MonadBase b m => MonadBase b (WebStateT conn sess st m) where
    liftBase = liftBaseDefault
instance MonadTransControl (WebStateT conn sess st) where
    type StT (WebStateT conn sess st) a = a
    liftWith = defaultLiftWith WebStateT runWebStateT
    restoreT = defaultRestoreT WebStateT
instance MonadBaseControl b m => MonadBaseControl b (WebStateT conn sess st m) where
    type StM (WebStateT conn sess st m) a = ComposeSt (WebStateT conn sess st) m a
    restoreM = defaultRestoreM
    liftBaseWith = defaultLiftBaseWith
type WebStateM conn sess st = WebStateT conn sess st (ResourceT IO)
type SessionId = T.Text
data Session conn sess st
    = Session
    { sess_id :: !SessionId
    , sess_validUntil :: !UTCTime
    , sess_data :: !sess
    , sess_safeActions :: !(SafeActionStore conn sess st)
    }
instance Show (Session conn sess st) where
    show = show . sess_id
data SessionManager conn sess st
   = SessionManager
   { sm_getSessionId :: forall ctx. SpockActionCtx ctx conn sess st SessionId
   , sm_regenerateSessionId :: forall ctx. SpockActionCtx ctx conn sess st ()
   , sm_readSession :: forall ctx. SpockActionCtx ctx conn sess st sess
   , sm_writeSession :: forall ctx. sess -> SpockActionCtx ctx conn sess st ()
   , sm_modifySession :: forall a ctx. (sess -> (sess, a)) -> SpockActionCtx ctx conn sess st a
   , sm_mapSessions :: forall ctx. (sess -> STM sess) -> SpockActionCtx ctx conn sess st ()
   , sm_clearAllSessions :: forall ctx. SpockActionCtx ctx conn sess st ()
   , sm_middleware :: Middleware
   , sm_addSafeAction :: forall ctx. PackedSafeAction conn sess st -> SpockActionCtx ctx conn sess st SafeActionHash
   , sm_lookupSafeAction :: forall ctx. SafeActionHash -> SpockActionCtx ctx conn sess st (Maybe (PackedSafeAction conn sess st))
   , sm_removeSafeAction :: forall ctx. PackedSafeAction conn sess st -> SpockActionCtx ctx conn sess st ()
   , sm_closeSessionManager :: IO ()
   }