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. Config -> Rep Config x)
-> (forall x. Rep Config x -> Config) -> Generic Config
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
config = (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Config -> Bool
forall (f :: Symbol) a s. HasField' f s a => s -> a
getField @"offline" Config
config) Bool -> Bool -> Bool
&& (Config -> 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
(Int -> ApplicationInfo -> ShowS)
-> (ApplicationInfo -> String)
-> ([ApplicationInfo] -> ShowS)
-> Show ApplicationInfo
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
(ApplicationInfo -> ApplicationInfo -> Bool)
-> (ApplicationInfo -> ApplicationInfo -> Bool)
-> Eq ApplicationInfo
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 KeyMap Text
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 _ "" info :: ApplicationInfo
info = ApplicationInfo
info
withApplicationValue name :: Text
name value :: Text
value info :: ApplicationInfo
info@(ApplicationInfo map :: KeyMap Text
map)
    | (Text
name Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["id", "version"]) Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
False = ApplicationInfo
info
    | Text -> Int
T.length (Text
value) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 64 = ApplicationInfo
info
    | ((Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ['a' .. 'z'] String -> ShowS
forall a. [a] -> [a] -> [a]
++ ['A' .. 'Z'] String -> ShowS
forall a. [a] -> [a] -> [a]
++ ['0' .. '9'] String -> ShowS
forall a. [a] -> [a] -> [a]
++ ['.', '-', '_']) (Text -> String
T.unpack Text
value)) Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
False = ApplicationInfo
info
    | Bool
otherwise = KeyMap Text -> ApplicationInfo
ApplicationInfo (KeyMap Text -> ApplicationInfo) -> KeyMap Text -> ApplicationInfo
forall a b. (a -> b) -> a -> b
$ Text -> Text -> KeyMap Text -> KeyMap Text
forall v. Text -> v -> HashMap Text v -> HashMap Text v
insertKey Text
name Text
value KeyMap Text
map

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