{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}

-- | This module provides types for defining notification events.
module Navi.Event.Types
  ( Event (..),
    AnyEvent (..),
    RepeatEvent (..),
    _NoRepeats,
    _AllowRepeats,
    ErrorNote (..),
    _NoErrNote,
    _AllowErrNote,
    EventError (..),
    EventSuccess (..),
  )
where

import Navi.Data.NaviNote (NaviNote)
import Navi.Data.PollInterval (PollInterval)
import Navi.Prelude
import Navi.Services.Types (ServiceType)

-- | Determines if we are allowed to send off duplicate notifications
-- simultaneously. If we are not, then 'NoRepeats' holds the last trigger
-- so that we can detect duplicates.
data RepeatEvent a
  = NoRepeats !(IORef (Maybe a))
  | AllowRepeats

makePrisms ''RepeatEvent

instance Show (RepeatEvent a) where
  show :: RepeatEvent a -> String
show (NoRepeats IORef (Maybe a)
_) = String
"NoRepeats <ref>"
  show RepeatEvent a
AllowRepeats = String
"AllowRepeats"
  {-# INLINEABLE show #-}

-- | Determines if we should send notifications for errors and, if so, if we
-- allow repeats.
data ErrorNote
  = NoErrNote
  | AllowErrNote !(RepeatEvent ())
  deriving stock (Int -> ErrorNote -> ShowS
[ErrorNote] -> ShowS
ErrorNote -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ErrorNote] -> ShowS
$cshowList :: [ErrorNote] -> ShowS
show :: ErrorNote -> String
$cshow :: ErrorNote -> String
showsPrec :: Int -> ErrorNote -> ShowS
$cshowsPrec :: Int -> ErrorNote -> ShowS
Show)

makePrisms ''ErrorNote

-- | Represents an error when querying an 'Event'.
data EventError = MkEventError
  { -- | The name of the event.
    EventError -> Text
name :: !Text,
    -- | Short description of the error.
    EventError -> Text
short :: !Text,
    -- | Long description of the error.
    EventError -> Text
long :: !Text
  }
  deriving stock (EventError -> EventError -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: EventError -> EventError -> Bool
$c/= :: EventError -> EventError -> Bool
== :: EventError -> EventError -> Bool
$c== :: EventError -> EventError -> Bool
Eq, Int -> EventError -> ShowS
[EventError] -> ShowS
EventError -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [EventError] -> ShowS
$cshowList :: [EventError] -> ShowS
show :: EventError -> String
$cshow :: EventError -> String
showsPrec :: Int -> EventError -> ShowS
$cshowsPrec :: Int -> EventError -> ShowS
Show)
  deriving anyclass (Show EventError
Typeable EventError
SomeException -> Maybe EventError
EventError -> String
EventError -> SomeException
forall e.
Typeable e
-> Show e
-> (e -> SomeException)
-> (SomeException -> Maybe e)
-> (e -> String)
-> Exception e
displayException :: EventError -> String
$cdisplayException :: EventError -> String
fromException :: SomeException -> Maybe EventError
$cfromException :: SomeException -> Maybe EventError
toException :: EventError -> SomeException
$ctoException :: EventError -> SomeException
Exception)

makeFieldLabelsNoPrefix ''EventError

-- | 'Event' represents sending notifications. An event will:
--
-- 1. Query for information (i.e. run a shell command).
-- 2. Parse the result.
-- 3. Raise an alert if the result matches some condition.
data Event result = MkEvent
  { -- | The name of this event.
    forall result. Event result -> Text
name :: !Text,
    -- | The service to run.
    forall result. Event result -> ServiceType result
serviceType :: !(ServiceType result),
    -- | How often to poll for this event, in seconds.
    forall result. Event result -> PollInterval
pollInterval :: !PollInterval,
    -- | Conditionally raises an alert based on the result.
    forall result. Event result -> result -> Maybe NaviNote
raiseAlert :: result -> Maybe NaviNote,
    -- | Determines how we handle repeat alerts.
    forall result. Event result -> RepeatEvent result
repeatEvent :: !(RepeatEvent result),
    -- | Determines how we handle errors.
    forall result. Event result -> ErrorNote
errorNote :: !ErrorNote
  }

makeFieldLabelsNoPrefix ''Event

instance Show (Event result) where
  show :: Event result -> String
show Event result
event =
    String
"MkEvent {name = "
      forall a. Semigroup a => a -> a -> a
<> Text -> String
unpack (Event result
event forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "name" a => a
#name)
      forall a. Semigroup a => a -> a -> a
<> String
", parser = <func>, raiseAlert = <func>, repeatEvent = "
      forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show (Event result
event forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "repeatEvent" a => a
#repeatEvent)
      forall a. Semigroup a => a -> a -> a
<> String
", errorNote = "
      forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show (Event result
event forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. forall a. IsLabel "errorNote" a => a
#errorNote)
      forall a. Semigroup a => a -> a -> a
<> String
"}"
  {-# INLINEABLE show #-}

-- | Existentially quantifies result type on an 'Event'. Used so that we can
-- store different events in the same list.
type AnyEvent :: Type
data AnyEvent where
  MkAnyEvent :: (Eq result, Show result) => Event result -> AnyEvent

deriving stock instance Show AnyEvent

-- | Holds the 'Event' data used after an event is successfully run.
--
-- @since 0.1
data EventSuccess result = MkEventSuccess
  { forall result. EventSuccess result -> result
result :: result,
    forall result. EventSuccess result -> RepeatEvent result
repeatEvent :: RepeatEvent result,
    forall result. EventSuccess result -> result -> Maybe NaviNote
raiseAlert :: result -> Maybe NaviNote
  }

makeFieldLabelsNoPrefix ''EventSuccess