{-# LANGUAGE OverloadedStrings #-}
module Web.Mackerel.Config
  ( Config(..)
  , HostStatusConfig(..)
  , loadConfig
  , parseConfig
  , loadClient
  , mackerelRoot
  , confFile
  , pidFile
  ) where

import Data.Aeson
import Data.Aeson.Types (Value(..), Result(..), typeMismatch)
import Data.Default (Default(..))
import Data.List (isPrefixOf, isInfixOf)
import Data.Maybe (fromMaybe)
import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import System.Directory (getHomeDirectory)
import System.FilePath ((</>))
import System.Info (os)
import Text.Toml (parseTomlDoc)

import Web.Mackerel.Client
import Web.Mackerel.Types.Host

data Config
  = Config {
    configApiBase :: Maybe String,
    configApiKey :: Maybe String,
    configRoot :: Maybe String,
    configPidfile :: Maybe String,
    configRoles :: Maybe [String],
    configVerbose :: Maybe Bool,
    configDiagnostic :: Maybe Bool,
    configDisplayname :: Maybe String,
    configHostStatus :: Maybe HostStatusConfig,
    configHttpProxy :: Maybe String
  } deriving (Eq, Show)

instance FromJSON Config where
  parseJSON (Object o)
    = Config <$> o .:? "apibase" <*> o .:? "apikey" <*> o .:? "root"
             <*> o .:? "pidfile" <*> o .:? "roles" <*> o .:? "verbose"
             <*> o .:? "diagnostic" <*> o .:? "display_name" <*> o .:? "host_status"
             <*> o .:? "http_proxy"
  parseJSON o = typeMismatch "Config" o

instance Default Config where
  def = Config def def def def def def def def def def

data HostStatusConfig
  = HostStatusConfig {
    hostStatusOnStart :: Maybe HostStatus,
    hostStatusOnStop :: Maybe HostStatus
  } deriving (Eq, Show)

instance FromJSON HostStatusConfig where
  parseJSON (Object o)
    = HostStatusConfig <$> o .:? "on_start" <*> o .:? "on_stop"
  parseJSON o = typeMismatch "HostStatusConfig" o

instance Default HostStatusConfig where
  def = HostStatusConfig def def

agentName :: String
agentName = "mackerel-agent"

loadConfig :: IO (Either String Config)
loadConfig = parseConfig <$> (Text.readFile =<< confFile)

parseConfig :: Text.Text -> Either String Config
parseConfig cnt
  = case parseTomlDoc "" cnt of
         Left _ -> Left "toml parse error"
         Right res -> case fromJSON $ toJSON res of
                           Error err -> Left err
                           Success r -> Right r

loadClient :: IO (Either String Client)
loadClient = fmap (\c -> def { apiKey = fromMaybe (apiKey def) $ configApiKey c,
                               apiBase = fromMaybe (apiBase def) $ configApiBase c }) <$> loadConfig

mackerelRoot :: IO FilePath
mackerelRoot | isBSD || isDarwin = getHomeDirectory >>= \home -> return $ home </> "Library" </> agentName
             | isWindows = error "Windows is not supported yet."
             | otherwise = return "/var/lib/mackerel-agent"

confFile :: IO FilePath
confFile | isLinux = return $ "/etc" </> agentName </> agentName ++ ".conf"
         | otherwise = mackerelRoot >>= \root -> return $ root </> agentName ++ ".conf"

pidFile :: IO FilePath
pidFile | isLinux = return "/var/run/mackerel-agent.pid"
        | otherwise = mackerelRoot >>= \root -> return $ root </> agentName ++ ".pid"

isBSD :: Bool
isBSD = "bsd" `isInfixOf` os

isDarwin :: Bool
isDarwin = os == "darwin"

isWindows :: Bool
isWindows = "mingw" `isPrefixOf` os

isLinux :: Bool
isLinux = not isBSD && not isDarwin && not isWindows