{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE TemplateHaskell            #-}
module Network.Datadog.Types where
import           Data.ByteString.Char8 (ByteString)
import           Data.DList (DList)
import           Data.HashMap.Strict (HashMap)
import           Data.Semigroup
import           Data.Text (Text)
import qualified Data.Text as T
import           Data.Int (Int64)
import           Data.Time.Clock (UTCTime, NominalDiffTime)
import           Data.Time.Clock.POSIX (POSIXTime)
import           Network.HTTP.Client (Manager)

newtype Timestamp = Timestamp { fromTimestamp :: NominalDiffTime }

newtype Write = Write { writeApiKey :: ByteString }

data ReadWrite = ReadWrite { readWriteApiKey :: ByteString
                           , readWriteApplicationKey :: ByteString
                           }

data DatadogClient a = DatadogClient
  { datadogClientManager        :: Manager
  , datadogClientKeys           :: a
  }


-- | Wraps the keys needed by Datadog to fully access the API.
data Keys = Keys { apiKey :: String
                   -- A write-key associated with a user
                 , appKey :: String
                   -- A read-key associated with an application
                 } deriving (Eq)


-- | An Environment contains everything needed to interact with Datadog.
data Environment = Environment { environmentKeys :: Keys
                                 -- ^ Auth keys to permit communication with Datadog
                               , environmentApiUrl :: String
                                 -- ^ The root URL for the Datadog API
                               , environmentManager :: Manager
                                 -- ^ HTTP manager used to make requests to Datadog
                               }

-- | Entity descriptor.
--
-- Entities in Datadog (hosts, metrics, events, etc) are frequently associated
-- with one more more "tags". These tags are labels that identify an entity as
-- belonging to a particular group or having particular properties. A tag can
-- come in two forms: a simple text label, describing entities associated with
-- the tag, or a key-value pair, associating entities with a specific slice of
-- a larger categorization.
--
-- As strings, the key and value parts of a key-value pair are separated by a
-- (':'). As such, any tag with no colons is a label, and any tag with one (or
-- more) is a key-value pair - if more than one ':' is specified, the
-- additional ':'s will become part of the value.
data Tag = KeyValueTag Text Text
         | LabelTag Text
         deriving (Eq)

instance Show Tag where
  show (KeyValueTag k v) = T.unpack k ++ (':' : T.unpack v)
  show (LabelTag t) = T.unpack t

instance Read Tag where
  readsPrec _ s = let t = T.pack s
                  in (\a -> [(a, "")]) $
                     maybe (LabelTag t) (\i -> uncurry KeyValueTag (T.splitAt i t)) $
                     T.findIndex (==':') t

-- | The status of a service, based on a check that is run against it.
data CheckStatus = CheckOk
                   -- ^ Everything is as it should be.
                 | CheckWarning
                   -- ^ Something abnormal, but not critical, is amiss.
                 | CheckCritical
                   -- ^ Something dangerously critical is amiss.
                 | CheckUnknown
                   -- ^ The current status cannot be determined.
                 deriving (Eq)

-- | The result of running a check on some service.
data CheckResult = CheckResult { checkResultCheck :: Text
                                 -- ^ Text describing the check
                               , checkResultHostName :: Text
                                 -- ^ Name of the host which the check applies to
                               , checkResultStatus :: CheckStatus
                                 -- ^ Status result of the check
                               , checkResultTimestamp :: Maybe UTCTime
                                 -- ^ Time at which the check occurred (Nothing will wait until the
                                 -- check is sent to Datadog to compute the time)
                               , checkResultMessage :: Maybe Text
                                 -- ^ Information related to why this specific check run supplied
                                 -- the status it did
                               , checkResultTags :: [Tag]
                                 -- ^ Tags to associate with this check run
                               } deriving (Eq)

-- | A description of when downtime should occur.
data DowntimeSpec = DowntimeSpec { downtimeSpecStart :: Maybe UTCTime
                                   -- ^ When to start the downtime (or immediately)
                                 , downtimeSpecEnd :: Maybe UTCTime
                                   -- ^ When to stop the downtime (or indefinitely)
                                 , downtimeSpecMessage :: Maybe Text
                                   -- ^ A message to include with notifications for this downtime
                                 , downtimeSpecScope :: Tag
                                   -- ^ The scope to apply downtime to (if applying downtime to a
                                   -- host, use a tag of the form "host:hostname", NOT just
                                   -- "hostname")
                                 } deriving (Eq)


-- | Datadog's internal reference to a specific donwtime instance.
type DowntimeId = Int


-- | A scheduled donwtime stored in Datadog.
data Downtime = Downtime { downtimeId' :: DowntimeId
                           -- ^ Datadog's unique reference to the scheduled downtime
                         , downtimeSpec :: DowntimeSpec
                           -- ^ Context on the downtime schedule
                         } deriving (Eq)


-- | A set of priorities used to denote the importance of an event.
data EventPriority = NormalPriority
                   | LowPriority
                   deriving (Eq)

instance Show EventPriority where
  show NormalPriority = "normal"
  show LowPriority = "low"

-- | The failure levels for an alert.
data AlertType = Error
               | Warning
               | Info
               | Success
               deriving (Eq)

instance Show AlertType where
  show Error = "error"
  show Warning = "warning"
  show Info = "info"
  show Success = "success"

-- | A source from which an event may originate, recognized by Datadog.
data SourceType = Nagios
                | Hudson
                | Jenkins
                | User
                | MyApps
                | Feed
                | Chef
                | Puppet
                | Git
                | BitBucket
                | Fabric
                | Capistrano
                deriving (Eq)

instance Show SourceType where
  show Nagios = "nagios"
  show Hudson = "hudson"
  show Jenkins = "jenkins"
  show User = "user"
  show MyApps = "my apps"
  show Feed = "feed"
  show Chef = "chef"
  show Puppet = "puppet"
  show Git = "git"
  show BitBucket = "bitbucket"
  show Fabric = "fabric"
  show Capistrano = "capistrano"

-- | Details that describe an event.
data EventSpec = EventSpec { eventSpecTitle :: Text
                           , eventSpecText :: Text
                             -- ^ The description/body of the event
                           , eventSpecDateHappened :: UTCTime
                             -- ^ The time at which the event occurred
                           , eventSpecPriority :: EventPriority
                           , eventSpecHost :: Maybe Text
                             -- ^ The hostname associated with the event
                           , eventSpecTags :: [Tag]
                           , eventSpecAlertType :: AlertType
                           , eventSpecSourceType :: Maybe SourceType
                             -- ^ The trigger of the event (if identifiable)
                           } deriving (Eq, Show)

-- | Datadog's internal reference to a specific event.
type EventId = Int

-- | An event stored within Datadog. An event represents some sort of
-- occurrence that was recorded in Datadog.
data Event = Event { eventId' :: EventId
                     -- ^ Datadog's unique reference to the event
                   , eventDetails :: EventSpec
                     -- ^ Context on what happened during this event
                   } deriving (Eq, Show)

data WrappedEvent = WrappedEvent { wrappedEvent :: Event }

data WrappedEvents = WrappedEvents { wrappedEvents :: [Event] }

newtype Series = Series { fromSeries :: DList Metric }
                 deriving (Semigroup, Monoid)

data MetricPoints = Gauge   [(POSIXTime, Float)]
                  | Counter [(POSIXTime, Int64)]

data Metric = Metric
  { metricName   :: Text
  , metricPoints :: MetricPoints
  , metricHost   :: Maybe Text
  , metricTags   :: [Text]
  }

-- | Each monitor is of a specific type, which determines what sort of check
-- the monitor performs.
data MonitorType = MetricAlert
                   -- ^ Watches a (combination of) metric(s), alerting when it
                   -- crosses some threshold.
                 | ServiceCheck
                   -- ^ Watches a service and alerts when the service enters a
                   -- failing state.
                 | EventAlert
                   -- ^ Checks the event stream for events meeting certain
                   -- criteria.
                 deriving (Eq)

instance Show MonitorType where
  show MetricAlert = "metric alert"
  show ServiceCheck = "service check"
  show EventAlert = "event alert"

-- | Advanced configuration parameters for a monitor.
data MonitorOptions = MonitorOptions { monitorOptionsSilenced :: HashMap T.Text (Maybe Integer)
                                     , monitorOptionsNotifyNoData :: Bool
                                     , monitorOptionsNoDataTimeframe :: Maybe Integer
                                     , monitorOptionsTimeoutH :: Maybe Integer
                                     , monitorOptionsRenotifyInterval :: Maybe Integer
                                     , monitorOptionsEscalationMessage :: T.Text
                                     , monitorOptionsNotifyAudit :: Bool
                                     } deriving (Eq)

-- | A representation of a monitor's configuration, from which a monitor could
-- be rebuilt.
data MonitorSpec = MonitorSpec { monitorSpecType' :: MonitorType
                               , monitorSpecQuery :: T.Text
                                 -- ^ The query string the monitor uses to
                                 -- determine its state.
                               , monitorSpecName :: Maybe T.Text
                                 -- ^ The human-readable name of the monitor.
                               , monitorSpecMessage :: Maybe T.Text
                                 -- ^ The message sent with the notification
                                 -- when the monitor is triggered.
                               , monitorSpecOptions :: MonitorOptions
                                 -- ^ Optional configuration parameters
                                 -- specifying advanced monitor beahviour.
                               } deriving (Eq)

-- | Datadog's internal reference to a specific monitor.
type MonitorId = Int

-- | A Datadog monitor. These monitors actively check multiple different types
-- of data within Datadog against user-provided conditions, triggering
-- notifications when condition(s) are met.
data Monitor = Monitor { monitorId' :: MonitorId
                         -- ^ Datadog's internal reference to this specific
                         -- monitor.
                       , monitorSpec :: MonitorSpec
                         -- ^ The specification from which this monitor can be
                         -- re-created.
                       } deriving (Eq)