{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}

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

import Data.Set (Set)
import GHC.Show (showSpace)
import Navi.Data.NaviNote (NaviNote)
import Navi.Data.PollInterval (PollInterval)
import Navi.Event.Types.EventError (EventError (MkEventError, long, name, short))
import Navi.Prelude
import Navi.Services.Types (ServiceType)
import Text.Show (Show (showsPrec), showParen, showString)

-- | 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))
  | SomeRepeats (Set a) (IORef (Maybe a))
  | AllowRepeats

instance (Show a) => Show (RepeatEvent a) where
  showsPrec :: Int -> RepeatEvent a -> ShowS
showsPrec Int
_ (NoRepeats IORef (Maybe a)
_) = String -> ShowS
showString String
"NoRepeats <ref>"
  showsPrec Int
i (SomeRepeats Set a
st IORef (Maybe a)
_) =
    Bool -> ShowS -> ShowS
showParen
      (Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
11)
      ( String -> ShowS
showString String
"SomeRepeats"
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ShowS
showSpace
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Int -> Set a -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
i Set a
st
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ShowS
showSpace
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. String -> ShowS
showString String
"<ref>"
      )
  showsPrec Int
_ RepeatEvent a
AllowRepeats = String -> ShowS
showString String
"AllowRepeats"

-- | 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
(Int -> ErrorNote -> ShowS)
-> (ErrorNote -> String)
-> ([ErrorNote] -> ShowS)
-> Show ErrorNote
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ErrorNote -> ShowS
showsPrec :: Int -> ErrorNote -> ShowS
$cshow :: ErrorNote -> String
show :: ErrorNote -> String
$cshowList :: [ErrorNote] -> ShowS
showList :: [ErrorNote] -> ShowS
Show)

-- | 'Event' represents sending notifications. An event will:
--
-- 1. Query for information (i.e. run a shell command).
-- 2. Parse the result.
-- 3. Map the result to a trigger.
-- 4. Raise an alert if the trigger matches some condition.
--
-- For most services, the (result -> trigger) map will be trivial i.e.
-- result and trigger will be identical.
--
-- For example, custom servicess is 'Text', and NetInterfaces is
-- NetInterface. In these cases, there is an exact
-- correspondonce between the service query result and the trigger.
--
-- But for e.g. battery percentage, the result is Battery (percentage and
-- status) whereas trigger is PercentageData.
--
-- See NOTE: [Battery Percentage Result/Trigger] for an explanation on why
-- this might be desirable.
data Event result trigger = MkEvent
  { -- | Determines how we handle errors.
    forall result trigger. Event result trigger -> ErrorNote
errorNote :: ErrorNote,
    -- | The name of this event.
    forall result trigger. Event result trigger -> Text
name :: Text,
    -- | How often to poll for this event, in seconds.
    forall result trigger. Event result trigger -> PollInterval
pollInterval :: PollInterval,
    -- | Conditionally raises an alert based on the (result -> trigger)
    -- mapping.
    forall result trigger.
Event result trigger -> result -> Maybe (trigger, NaviNote)
raiseAlert :: result -> Maybe (trigger, NaviNote),
    -- | Determines how we handle repeat alerts.
    forall result trigger. Event result trigger -> RepeatEvent trigger
repeatEvent :: RepeatEvent trigger,
    -- | The service to run.
    forall result trigger. Event result trigger -> ServiceType result
serviceType :: ServiceType result
  }

makeFieldLabelsNoPrefix ''Event

instance (Show trigger) => Show (Event result trigger) where
  showsPrec :: Int -> Event result trigger -> ShowS
showsPrec Int
i Event result trigger
event =
    Bool -> ShowS -> ShowS
showParen
      (Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
11)
      ( String -> ShowS
showString String
"MkEvent {errorNote = "
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Int -> ErrorNote -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
i (Event result trigger
event Event result trigger
-> Optic' A_Lens NoIx (Event result trigger) ErrorNote -> ErrorNote
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx (Event result trigger) ErrorNote
#errorNote)
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. String -> ShowS
showString String
", name = "
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Int -> Text -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
i (Event result trigger
event Event result trigger
-> Optic' A_Lens NoIx (Event result trigger) Text -> Text
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx (Event result trigger) Text
#name)
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. String -> ShowS
showString String
", pollInterval = "
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Int -> PollInterval -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
i (Event result trigger
event Event result trigger
-> Optic' A_Lens NoIx (Event result trigger) PollInterval
-> PollInterval
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx (Event result trigger) PollInterval
#pollInterval)
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. String -> ShowS
showString String
", raiseAlert = <func>, repeatEvent = "
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Int -> RepeatEvent trigger -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
i (Event result trigger
event Event result trigger
-> Optic' A_Lens NoIx (Event result trigger) (RepeatEvent trigger)
-> RepeatEvent trigger
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx (Event result trigger) (RepeatEvent trigger)
#repeatEvent)
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. String -> ShowS
showString String
", serviceType = "
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Int -> ServiceType result -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
i (Event result trigger
event Event result trigger
-> Optic' A_Lens NoIx (Event result trigger) (ServiceType result)
-> ServiceType result
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx (Event result trigger) (ServiceType result)
#serviceType)
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. String -> ShowS
showString String
"}"
      )

-- | 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 :: (Ord trigger, Show result, Show trigger) => Event result trigger -> AnyEvent

deriving stock instance Show AnyEvent

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

makeFieldLabelsNoPrefix ''EventSuccess