-- | Module for small utility functions.
module Cardano.Wallet.Util
       ( -- * String manipulation
         headToLower
       , stripFieldPrefix
       , mkJsonKey

       -- * Miscellaneous
       , eitherToParser

       -- * Time
       , defaultApiTimeLocale
       , apiTimeFormat
       , parseApiUtcTime
       , showApiUtcTime
       ) where

import           Universum

import           Data.Aeson.Types (Parser)
import           Data.Char (isUpper, toLower)
import qualified Data.Time as Time
import           Formatting (bprint, build, formatToString, (%))
import qualified Formatting.Buildable
import qualified Prelude

-- * Miscellaneous

-- | Convert a given Either to an Aeson Parser
eitherToParser :: Buildable a => Either a b -> Parser b
eitherToParser =
    either (fail . formatToString build) pure


-- * String manipulation utils

headToLower :: String -> Maybe String
headToLower []     = Nothing
headToLower (x:xs) = Just $ toLower x : xs

stripFieldPrefix :: String -> String
stripFieldPrefix = dropWhile (not . isUpper) . drop 1

mkJsonKey :: String -> String
mkJsonKey s = fromMaybe s . headToLower $ stripFieldPrefix s

-- * Time

-- | Currently we support only American usage.
defaultApiTimeLocale :: Time.TimeLocale
defaultApiTimeLocale = Time.defaultTimeLocale

-- | Time format used in API. Corresponds to ISO-8601.
-- Example of time: "2018-03-07T16:20:27.477318"
--
-- Note: there is more neat support of time formats in time-1.9,
-- Data.Time.Format.ISO8601 module, but that version is barely applicable with
-- current LTS-9.1.
apiTimeFormat :: String
apiTimeFormat = Time.iso8601DateFormat (Just "%H:%M:%S%Q")

newtype UtcTimeParseError = UtcTimeParseError Text

instance Buildable UtcTimeParseError where
    build (UtcTimeParseError msg) = bprint ("UTC time parse error: "%build) msg

instance Show UtcTimeParseError where
    show = formatToString build

-- | Parse UTC time from API.
parseApiUtcTime :: Text -> Either UtcTimeParseError Time.UTCTime
parseApiUtcTime =
    first UtcTimeParseError .
    Time.parseTimeM False defaultApiTimeLocale apiTimeFormat .
    toString

-- | Encode UTC time for API.
showApiUtcTime :: Time.UTCTime -> Text
showApiUtcTime = toText . Time.formatTime defaultApiTimeLocale apiTimeFormat
