module Hydra.TUI.Drawing.EventHistoryTab where

import Hydra.Prelude

import Brick
import Brick.Widgets.Border (borderWithLabel, hBorderWithLabel)
import Brick.Widgets.List qualified as BrickList
import Data.Time (defaultTimeLocale, formatTime)
import Data.Time.LocalTime (TimeZone, utcToLocalTime)
import Hydra.TUI.Logging.Types (EventHistoryFilter (..), LogMessage (..), Severity (..))
import Hydra.TUI.Model
import Hydra.TUI.Style
import Lens.Micro ((^.))

-- | Render the Event History tab: a scrollable list of past log messages with
-- a detail pane showing the selected entry (summary or raw JSON).
drawEventHistoryTab :: RootState -> Widget Name
drawEventHistoryTab :: RootState -> Widget Text
drawEventHistoryTab RootState
s =
  Widget Text -> Widget Text -> Widget Text
forall n. Widget n -> Widget n -> Widget n
borderWithLabel (AttrName -> Widget Text -> Widget Text
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
neutral (Widget Text -> Widget Text) -> Widget Text -> Widget Text
forall a b. (a -> b) -> a -> b
$ Text -> Widget Text
forall n. Text -> Widget n
txt Text
headerLabel) (Widget Text -> Widget Text) -> Widget Text -> Widget Text
forall a b. (a -> b) -> a -> b
$
    [Widget Text] -> Widget Text
forall n. [Widget n] -> Widget n
vBox
      [ Int -> Widget Text -> Widget Text
forall n. Int -> Widget n -> Widget n
vLimitPercent Int
50 (Widget Text -> Widget Text) -> Widget Text -> Widget Text
forall a b. (a -> b) -> a -> b
$ (Bool -> LogMessage -> Widget Text)
-> Bool -> GenericList Text Vector LogMessage -> Widget Text
forall (t :: * -> *) n e.
(Traversable t, Splittable t, Ord n, Show n) =>
(Bool -> e -> Widget n) -> Bool -> GenericList n t e -> Widget n
BrickList.renderList (TimeZone -> Bool -> LogMessage -> Widget Text
drawEventListItem TimeZone
tz) Bool
True (RootState
s RootState
-> Getting
     (GenericList Text Vector LogMessage)
     RootState
     (GenericList Text Vector LogMessage)
-> GenericList Text Vector LogMessage
forall s a. s -> Getting a s a -> a
^. Getting
  (GenericList Text Vector LogMessage)
  RootState
  (GenericList Text Vector LogMessage)
Lens' RootState (GenericList Text Vector LogMessage)
eventHistoryListL)
      , Widget Text -> Widget Text
forall n. Widget n -> Widget n
hBorderWithLabel (AttrName -> Widget Text -> Widget Text
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
neutral (Widget Text -> Widget Text) -> Widget Text -> Widget Text
forall a b. (a -> b) -> a -> b
$ Text -> Widget Text
forall n. Text -> Widget n
txt Text
detailLabel)
      , Text -> ViewportType -> Widget Text -> Widget Text
forall n.
(Ord n, Show n) =>
n -> ViewportType -> Widget n -> Widget n
viewport Text
"event-detail" ViewportType
Vertical (Widget Text -> Widget Text) -> Widget Text -> Widget Text
forall a b. (a -> b) -> a -> b
$
          Int -> Widget Text -> Widget Text
forall n. Int -> Widget n -> Widget n
padLeftRight Int
1 (Widget Text -> Widget Text) -> Widget Text -> Widget Text
forall a b. (a -> b) -> a -> b
$
            TimeZone -> Bool -> Maybe (Int, LogMessage) -> Widget Text
drawEventDetail TimeZone
tz Bool
rawView (GenericList Text Vector LogMessage -> Maybe (Int, LogMessage)
forall (t :: * -> *) e n.
(Splittable t, Traversable t, Semigroup (t e)) =>
GenericList n t e -> Maybe (Int, e)
BrickList.listSelectedElement (RootState
s RootState
-> Getting
     (GenericList Text Vector LogMessage)
     RootState
     (GenericList Text Vector LogMessage)
-> GenericList Text Vector LogMessage
forall s a. s -> Getting a s a -> a
^. Getting
  (GenericList Text Vector LogMessage)
  RootState
  (GenericList Text Vector LogMessage)
Lens' RootState (GenericList Text Vector LogMessage)
eventHistoryListL))
      ]
 where
  tz :: TimeZone
tz = RootState
s RootState -> Getting TimeZone RootState TimeZone -> TimeZone
forall s a. s -> Getting a s a -> a
^. Getting TimeZone RootState TimeZone
Lens' RootState TimeZone
timeZoneL
  rawView :: Bool
rawView = RootState
s RootState -> Getting Bool RootState Bool -> Bool
forall s a. s -> Getting a s a -> a
^. Getting Bool RootState Bool
Lens' RootState Bool
eventDetailRawL
  detailLabel :: Text
detailLabel = if Bool
rawView then Text
" Detail (raw)  d:summary " else Text
" Detail  d:raw "
  headerLabel :: Text
headerLabel = case RootState
s RootState
-> Getting EventHistoryFilter RootState EventHistoryFilter
-> EventHistoryFilter
forall s a. s -> Getting a s a -> a
^. Getting EventHistoryFilter RootState EventHistoryFilter
Lens' RootState EventHistoryFilter
eventHistoryFilterL of
    EventHistoryFilter
ShowAll -> Text
" Event History "
    EventHistoryFilter
ErrorsOnly -> Text
" Event History · errors only (e:show all) "

-- | Render a single log entry for display in event lists.
-- Also used by 'drawMainTab' for the recent-events strip.
drawEventListItem :: TimeZone -> Bool -> LogMessage -> Widget Name
drawEventListItem :: TimeZone -> Bool -> LogMessage -> Widget Text
drawEventListItem TimeZone
tz Bool
selected (LogMessage{Text
message :: Text
message :: LogMessage -> Text
message, Severity
severity :: Severity
severity :: LogMessage -> Severity
severity, UTCTime
time :: UTCTime
time :: LogMessage -> UTCTime
time}) =
  let ts :: String
ts = TimeLocale -> String -> LocalTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%b %d %H:%M:%S" (TimeZone -> UTCTime -> LocalTime
utcToLocalTime TimeZone
tz UTCTime
time)
      line :: Widget Text
line = String -> Widget Text
forall n. String -> Widget n
str String
ts Widget Text -> Widget Text -> Widget Text
forall n. Widget n -> Widget n -> Widget n
<+> Text -> Widget Text
forall n. Text -> Widget n
txt Text
"  " Widget Text -> Widget Text -> Widget Text
forall n. Widget n -> Widget n -> Widget n
<+> Text -> Widget Text
forall n. Text -> Widget n
txt Text
severityIcon Widget Text -> Widget Text -> Widget Text
forall n. Widget n -> Widget n -> Widget n
<+> Text -> Widget Text
forall n. Text -> Widget n
txt Text
"  " Widget Text -> Widget Text -> Widget Text
forall n. Widget n -> Widget n -> Widget n
<+> Text -> Widget Text
forall n. Text -> Widget n
txt Text
message
      styled :: Widget Text
styled = case Severity
severity of
        Severity
Success | Bool -> Bool
not Bool
selected -> AttrName -> Widget Text -> Widget Text
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
infoA Widget Text
line
        Severity
Error | Bool -> Bool
not Bool
selected -> AttrName -> Widget Text -> Widget Text
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
negative Widget Text
line
        Severity
_ -> Widget Text
line
   in if Bool
selected then AttrName -> Widget Text -> Widget Text
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
BrickList.listSelectedAttr Widget Text
styled else Widget Text
styled
 where
  severityIcon :: Text
severityIcon = case Severity
severity of
    Severity
Success -> Text
"✓"
    Severity
Info -> Text
"·"
    Severity
Error -> Text
"✗"

-- | Render the detail pane for a selected event, either as a summary or the raw JSON.
drawEventDetail :: TimeZone -> Bool -> Maybe (Int, LogMessage) -> Widget Name
drawEventDetail :: TimeZone -> Bool -> Maybe (Int, LogMessage) -> Widget Text
drawEventDetail TimeZone
_ Bool
_ Maybe (Int, LogMessage)
Nothing = AttrName -> Widget Text -> Widget Text
forall n. AttrName -> Widget n -> Widget n
withAttr AttrName
neutral (Widget Text -> Widget Text) -> Widget Text -> Widget Text
forall a b. (a -> b) -> a -> b
$ Text -> Widget Text
forall n. Text -> Widget n
txt Text
"No event selected."
drawEventDetail TimeZone
tz Bool
rawView (Just (Int
_, LogMessage{Text
detail :: Text
detail :: LogMessage -> Text
detail, Severity
severity :: LogMessage -> Severity
severity :: Severity
severity, UTCTime
time :: LogMessage -> UTCTime
time :: UTCTime
time, Text
rawJson :: Text
rawJson :: LogMessage -> Text
rawJson})) =
  [Widget Text] -> Widget Text
forall n. [Widget n] -> Widget n
vBox
    [ [Widget Text] -> Widget Text
forall n. [Widget n] -> Widget n
hBox
        [ AttrName -> Widget Text -> Widget Text
forall n. AttrName -> Widget n -> Widget n
withAttr (Severity -> AttrName
severityToAttr Severity
severity) (Widget Text -> Widget Text) -> Widget Text -> Widget Text
forall a b. (a -> b) -> a -> b
$ Text -> Widget Text
forall n. Text -> Widget n
txt (Severity -> Text
severityLabel Severity
severity)
        , Text -> Widget Text
forall n. Text -> Widget n
txt Text
"  "
        , String -> Widget Text
forall n. String -> Widget n
str (String -> Widget Text) -> String -> Widget Text
forall a b. (a -> b) -> a -> b
$ TimeLocale -> String -> LocalTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%b %d %Y  %H:%M:%S" (TimeZone -> UTCTime -> LocalTime
utcToLocalTime TimeZone
tz UTCTime
time)
        ]
    , Text -> Widget Text
forall n. Text -> Widget n
txt Text
" "
    , Widget Text
body
    ]
 where
  severityLabel :: Severity -> Text
severityLabel = \case
    Severity
Success -> Text
"Success"
    Severity
Info -> Text
"Info"
    Severity
Error -> Text
"Error"
  body :: Widget Text
body
    | Bool
rawView = Text -> Widget Text
forall n. Text -> Widget n
txtWrap Text
rawJson
    | Bool
otherwise = Text -> Widget Text
forall n. Text -> Widget n
txtWrap Text
detail