module LaunchDarkly.Server.Config.Internal
    ( Config (..)
    , shouldSendEvents
    , ApplicationInfo
    , makeApplicationInfo
    , withApplicationValue
    , getApplicationInfoHeader
    ) where

import Control.Monad.Logger (LoggingT)
import Data.Generics.Product (getField)
import Data.Set (Set)
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)
import GHC.Natural (Natural)
import Network.HTTP.Client (Manager)

import Control.Lens ((&))
import Data.List (sortBy)
import Data.Ord (comparing)
import LaunchDarkly.AesonCompat (KeyMap, emptyObject, insertKey, toList)
import qualified LaunchDarkly.AesonCompat as AesonCompat
import LaunchDarkly.Server.DataSource.Internal (DataSourceFactory)
import LaunchDarkly.Server.Reference (Reference)
import LaunchDarkly.Server.Store (PersistentDataStore)

-- | Config allows advanced configuration of the LaunchDarkly client.
data Config = Config
    { Config -> Text
key :: !Text
    , Config -> Text
baseURI :: !Text
    , Config -> Text
streamURI :: !Text
    , Config -> Text
eventsURI :: !Text
    , Config -> Maybe PersistentDataStore
storeBackend :: !(Maybe PersistentDataStore)
    , Config -> Natural
storeTTLSeconds :: !Natural
    , Config -> Bool
streaming :: !Bool
    , Config -> Int
initialRetryDelay :: !Int
    , Config -> Bool
allAttributesPrivate :: !Bool
    , Config -> Set Reference
privateAttributeNames :: !(Set Reference)
    , Config -> Natural
flushIntervalSeconds :: !Natural
    , Config -> Natural
pollIntervalSeconds :: !Natural
    , Config -> Natural
contextKeyLRUCapacity :: !Natural
    , Config -> Natural
eventsCapacity :: !Natural
    , Config -> LoggingT IO () -> IO ()
logger :: !(LoggingT IO () -> IO ())
    , Config -> Bool
sendEvents :: !Bool
    , Config -> Bool
offline :: !Bool
    , Config -> Natural
requestTimeoutSeconds :: !Natural
    , Config -> Bool
useLdd :: !Bool
    , Config -> Maybe DataSourceFactory
dataSourceFactory :: !(Maybe DataSourceFactory)
    , Config -> Maybe Manager
manager :: !(Maybe Manager)
    , Config -> Maybe ApplicationInfo
applicationInfo :: !(Maybe ApplicationInfo)
    }
    deriving (forall x. Rep Config x -> Config
forall x. Config -> Rep Config x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Config x -> Config
$cfrom :: forall x. Config -> Rep Config x
Generic)

shouldSendEvents :: Config -> Bool
shouldSendEvents :: Config -> Bool
shouldSendEvents Config
config = (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (f :: Symbol) a s. HasField' f s a => s -> a
getField @"offline" Config
config) Bool -> Bool -> Bool
&& (forall (f :: Symbol) a s. HasField' f s a => s -> a
getField @"sendEvents" Config
config)

-- |
-- An object that allows configuration of application metadata.
--
-- Application metadata may be used in LaunchDarkly analytics or other product
-- features, but does not affect feature flag evaluations.
--
-- To use these properties, provide an instance of ApplicationInfo to the 'Config' with 'configSetApplicationInfo'.
newtype ApplicationInfo = ApplicationInfo (KeyMap Text) deriving (Int -> ApplicationInfo -> ShowS
[ApplicationInfo] -> ShowS
ApplicationInfo -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ApplicationInfo] -> ShowS
$cshowList :: [ApplicationInfo] -> ShowS
show :: ApplicationInfo -> String
$cshow :: ApplicationInfo -> String
showsPrec :: Int -> ApplicationInfo -> ShowS
$cshowsPrec :: Int -> ApplicationInfo -> ShowS
Show, ApplicationInfo -> ApplicationInfo -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ApplicationInfo -> ApplicationInfo -> Bool
$c/= :: ApplicationInfo -> ApplicationInfo -> Bool
== :: ApplicationInfo -> ApplicationInfo -> Bool
$c== :: ApplicationInfo -> ApplicationInfo -> Bool
Eq)

-- | Create a default instance
makeApplicationInfo :: ApplicationInfo
makeApplicationInfo :: ApplicationInfo
makeApplicationInfo = KeyMap Text -> ApplicationInfo
ApplicationInfo forall v. KeyMap v
emptyObject

-- |
-- Set a new name / value pair into the application info instance.
--
-- Values have the following restrictions:
-- - Cannot be empty
-- - Cannot exceed 64 characters in length
-- - Can only contain a-z, A-Z, 0-9, period (.), dash (-), and underscore (_).
--
-- Invalid values or unsupported keys will be ignored.
withApplicationValue :: Text -> Text -> ApplicationInfo -> ApplicationInfo
withApplicationValue :: Text -> Text -> ApplicationInfo -> ApplicationInfo
withApplicationValue Text
_ Text
"" ApplicationInfo
info = ApplicationInfo
info
withApplicationValue Text
name Text
value info :: ApplicationInfo
info@(ApplicationInfo KeyMap Text
map)
    | (Text
name forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"id", Text
"version"]) forall a. Eq a => a -> a -> Bool
== Bool
False = ApplicationInfo
info
    | Text -> Int
T.length (Text
value) forall a. Ord a => a -> a -> Bool
> Int
64 = ApplicationInfo
info
    | (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'a' .. Char
'z'] forall a. [a] -> [a] -> [a]
++ [Char
'A' .. Char
'Z'] forall a. [a] -> [a] -> [a]
++ [Char
'0' .. Char
'9'] forall a. [a] -> [a] -> [a]
++ [Char
'.', Char
'-', Char
'_']) (Text -> String
T.unpack Text
value)) forall a. Eq a => a -> a -> Bool
== Bool
False = ApplicationInfo
info
    | Bool
otherwise = KeyMap Text -> ApplicationInfo
ApplicationInfo forall a b. (a -> b) -> a -> b
$ forall v. Text -> v -> KeyMap v -> KeyMap v
insertKey Text
name Text
value KeyMap Text
map

getApplicationInfoHeader :: ApplicationInfo -> Maybe Text
getApplicationInfoHeader :: ApplicationInfo -> Maybe Text
getApplicationInfoHeader (ApplicationInfo KeyMap Text
values)
    | forall v. KeyMap v -> Bool
AesonCompat.null KeyMap Text
values = forall a. Maybe a
Nothing
    | Bool
otherwise =
        forall v. KeyMap v -> [(Text, v)]
toList KeyMap Text
values
            forall a b. a -> (a -> b) -> b
& forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing forall a b. (a, b) -> a
fst)
            forall a b. a -> (a -> b) -> b
& forall a b. (a -> b) -> [a] -> [b]
map forall {a}. (Semigroup a, IsString a) => (a, a) -> a
makeTag
            forall a b. a -> (a -> b) -> b
& [Text] -> Text
T.unwords
            forall a b. a -> (a -> b) -> b
& forall a. a -> Maybe a
Just
  where
    makeTag :: (a, a) -> a
makeTag (a
key, a
value) = a
"application-" forall a. Semigroup a => a -> a -> a
<> a
key forall a. Semigroup a => a -> a -> a
<> a
"/" forall a. Semigroup a => a -> a -> a
<> a
value