module Potoki.Hasql.Produce (
  vectorStatefulSession,
  statefulSession
) where

import           Potoki.Hasql.Prelude
import qualified Data.Vector               as D
import qualified Hasql.Connection          as F
import qualified Hasql.Session             as G
import qualified Potoki.Core.Fetch         as A
import           Potoki.Core.Produce
import qualified Potoki.Core.Transform     as J
import qualified Potoki.Hasql.Error.Hasql  as I
import           Potoki.Hasql.Error.Types
import qualified Acquire.Acquire           as B


vectorStatefulSession :: (state -> G.Session (Vector a, state)) -> state -> F.Settings -> Int -> Produce (Either Error a)
vectorStatefulSession vectorSession initialState connectionConfig buffering =
  transform
    (right' (J.takeWhile (not . D.null) >>> J.vector >>> J.bufferize buffering))
    (statefulSession vectorSession initialState connectionConfig)

statefulSession :: (state -> G.Session (a, state)) -> state -> F.Settings -> Produce (Either Error a)
statefulSession session initialState =
  havingConnection $ \ connection -> do
    stateRef <- newIORef initialState
    return $ A.Fetch $ do
      state <- readIORef stateRef
      sessionResult <- G.run (session state) connection
      case sessionResult of
        Left err -> return (Just (Left (I.sessionError err)))
        Right (result, newState) -> do
          writeIORef stateRef newState
          return (Just (Right result))

havingConnection :: (F.Connection -> IO (A.Fetch (Either Error a))) -> F.Settings -> Produce (Either Error a)
havingConnection cont connectionSettings =
  Produce $ B.Acquire $ do
    errorOrConnection <- F.acquire connectionSettings
    case errorOrConnection of
      Left err ->
        let
          fetch =
            A.Fetch $ return (Just (Left (I.connectionError err)))
          kill =
            return ()
          in return (fetch, kill)
      Right connection -> do
        fetch <- cont connection
        let kill = F.release connection
        return (fetch, kill)