{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module System.Etc.Internal.Resolver.Default (resolveDefault) where

import Protolude

import qualified Data.Aeson          as JSON
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Set            as Set

import qualified System.Etc.Internal.Spec.Types as Spec
import           System.Etc.Internal.Types

toDefaultConfigValue :: JSON.Value -> ConfigValue
toDefaultConfigValue =
  ConfigValue . Set.singleton . Default

buildDefaultResolver :: Spec.ConfigSpec cmd -> Maybe ConfigValue
buildDefaultResolver spec =
  let
    resolverReducer :: Text -> Spec.ConfigValue cmd -> Maybe ConfigValue -> Maybe ConfigValue
    resolverReducer specKey specValue mConfig =
      case specValue of
        Spec.ConfigValue def _ ->
          let
            mConfigSource =
              toDefaultConfigValue <$> def

            updateConfig =
              writeInSubConfig specKey <$> mConfigSource <*> mConfig
          in
            updateConfig <|> mConfig

        Spec.SubConfig specConfigMap ->
          let
            mSubConfig =
              specConfigMap
              & HashMap.foldrWithKey
                    resolverReducer
                    (Just emptySubConfig)
              & filterMaybe isEmptySubConfig

            updateConfig =
              writeInSubConfig specKey <$> mSubConfig <*> mConfig
          in
            updateConfig <|> mConfig
  in
    Spec.specConfigValues spec
    & HashMap.foldrWithKey
          resolverReducer
          (Just emptySubConfig)
    & filterMaybe isEmptySubConfig

{-|

Gathers all default values from the @etc/spec@ entries inside a @ConfigSpec@

-}
resolveDefault
  :: Spec.ConfigSpec cmd -- ^ ConfigSpec
  -> Config              -- ^ returns Configuration Map with default values included
resolveDefault spec =
  maybe (Config emptySubConfig)
        Config
        (buildDefaultResolver spec)