module LaunchDarkly.Server.DataSource.Internal
    ( DataSourceFactory
    , nullDataSourceFactory
    , DataSource(..)
    , DataSourceUpdates(..)
    , defaultDataSourceUpdates
    )
    where

import Data.IORef          (IORef, atomicModifyIORef')
import Data.Text           (Text)
import GHC.Natural         (Natural)

import LaunchDarkly.Server.Config.ClientContext (ClientContext)
import LaunchDarkly.Server.Client.Status        (Status, transitionStatus)
import LaunchDarkly.Server.Features             (Segment, Flag)
import LaunchDarkly.Server.Store.Internal       (initializeStore, insertFlag, insertSegment, deleteFlag, deleteSegment, StoreHandle)
import LaunchDarkly.AesonCompat                 (KeyMap)

type DataSourceFactory = ClientContext -> DataSourceUpdates -> IO DataSource

nullDataSourceFactory :: DataSourceFactory
nullDataSourceFactory _ _ =
    pure $ DataSource (pure False) (pure ()) (pure ())

data DataSource = DataSource
    { dataSourceIsInitialized :: IO Bool
    , dataSourceStart :: IO ()
    , dataSourceStop :: IO ()
    }

data DataSourceUpdates = DataSourceUpdates
    { dataSourceUpdatesInit :: !(KeyMap Flag -> KeyMap Segment -> IO (Either Text ()))
    , dataSourceUpdatesInsertFlag :: !(Flag -> IO (Either Text ()))
    , dataSourceUpdatesInsertSegment :: !(Segment -> IO (Either Text ()))
    , dataSourceUpdatesDeleteFlag :: !(Text -> Natural -> IO (Either Text ()))
    , dataSourceUpdatesDeleteSegment :: !(Text -> Natural -> IO (Either Text ()))
    , dataSourceUpdatesSetStatus :: Status -> IO ()
    }

defaultDataSourceUpdates :: IORef Status -> StoreHandle IO -> DataSourceUpdates
defaultDataSourceUpdates status store =
    let modifyStatus status' = atomicModifyIORef' status (fmap (,()) (transitionStatus status'))
    in DataSourceUpdates
        { dataSourceUpdatesInit = initializeStore store
        , dataSourceUpdatesInsertFlag = insertFlag store
        , dataSourceUpdatesInsertSegment = insertSegment store
        , dataSourceUpdatesDeleteFlag = deleteFlag store
        , dataSourceUpdatesDeleteSegment = deleteSegment store
        , dataSourceUpdatesSetStatus = modifyStatus
        }