module Database.Redis.Config
       ( RedisConfig(..)
       , connectRedis
       ) where

import Control.Applicative
import Data.Aeson
import Data.Scientific
import Data.Time
import Database.Redis

import qualified Data.Aeson.Types as A
import qualified Data.ByteString.Char8 as BS
import qualified Data.Text.Encoding as T


{- | Wrapper around 'ConnectInfo' to parse connection settings from
JSON-like values. All fields are optional, defaults are get from
'defaultConnectInfo'

Here is YAML example of config:

@
host: localhost                 # host name or address
port: 6379                      # you can specify either port
\# socket: \/run\/redis.socket     # or unix socket path
\# service: redis                # or service name
password: "pass"                # if not specified then no password used
database: 0                     # database number to connect to
max-connections: 5              # max 5 connections in pool
max-idle-time: 30               # keep connection open for 30 seconds
@

-}

newtype RedisConfig =
    RedisConfig
    { getConnectInfo :: ConnectInfo
    }


parsePort :: Object -> A.Parser (Maybe PortID)
parsePort o =
    optional
    $   (fmap (\a -> PortNumber $ floor (a :: Scientific)) (o .: "port"))
    <|> (fmap UnixSocket (o .: "socket"))
    <|> (fmap Service (o .: "service"))

parsePassword :: Object -> A.Parser (Maybe BS.ByteString)
parsePassword o = do
  mp <- o .:? "password"
  pure $ case mp of
    Nothing -> Nothing
    Just "" -> Nothing
    Just ps -> Just $ T.encodeUtf8 ps

instance FromJSON RedisConfig where
    parseJSON v = RedisConfig <$> withObject "RedisConfig" go v
      where
        go o =
            ConnInfo
            <$> (o .:? "host" .!= connectHost defaultConnectInfo)
            <*> (parsePort o .!= connectPort defaultConnectInfo)
            <*> parsePassword o
            <*> (o .:? "database" .!= (connectDatabase defaultConnectInfo))
            <*> (o .:? "max-connections" .!= (connectMaxConnections defaultConnectInfo))
            <*> (fmap (fmap (realToFrac :: Scientific -> NominalDiffTime)) (o .:? "max-idle-time") .!= (connectMaxIdleTime defaultConnectInfo))


-- | Open redis connection
connectRedis :: RedisConfig -> IO Connection
connectRedis = connect . getConnectInfo