module Network.Mattermost.Logging
( -- * Logging-Related Types
  Logger
, LogEvent(..)
, LogEventType(..)
  -- * Basic Loggers
, mmLoggerInfo
, mmLoggerInfoFilter
, mmLoggerDebug
, mmLoggerDebugFilter
  -- ** @stderr@ variants
, mmLoggerInfoErr
, mmLoggerInfoFilterErr
, mmLoggerDebugErr
, mmLoggerDebugFilterErr
) where

import Control.Monad (when)
import Data.Time.Clock (getCurrentTime)
import System.IO (Handle, hFlush, hPutStr, stderr
                 , hIsSeekable, hSeek, SeekMode(..))

import Network.Mattermost.Types.Base

-- | 'mmLoggerDebugFilter' is the same as 'mmLoggerDebug' but takes
--   a user-defined predicate that it uses to select which events to
--   log before writing them to the provided 'Handle'
mmLoggerDebugFilter :: (LogEvent -> Bool) -> Handle -> Logger
mmLoggerDebugFilter p h l
  | p l       = mmLoggerDebug h l
  | otherwise = return ()

-- | 'mmLoggerDebug' prints the full data of every logging event to
--   the provided 'Handle'.
mmLoggerDebug :: Handle -> Logger
mmLoggerDebug h LogEvent { logFunction = f, logEventType = e } = do
  now <- getCurrentTime
  canSeek <- hIsSeekable h
  when canSeek $ hSeek h SeekFromEnd 0
  mapM_ (hPutStr h)
    [ "[", show now, "] ", f, ": ", show e, "\n" ]
  hFlush h

-- | 'mmLoggerDebugErr' prints the full data of every logging event
--   to 'stderr'.
mmLoggerDebugErr :: Logger
mmLoggerDebugErr = mmLoggerDebug stderr

-- | 'mmLoggerDebugFilterErr' takes a user-defined predicate that
--   it uses to select which events to log before logging them to
--   'stderr'.
mmLoggerDebugFilterErr :: (LogEvent -> Bool) -> Logger
mmLoggerDebugFilterErr p l = mmLoggerDebugFilter p stderr l


-- | 'mmLoggerInfoFilter' is the same as 'mmLoggerInfo' but takes
--   a user-defined predicate that it uses to select which events to
--   log before writing them to the provided 'Handle'
mmLoggerInfoFilter :: (LogEvent -> Bool) -> Handle -> Logger
mmLoggerInfoFilter p h l
  | p l       = mmLoggerInfo h l
  | otherwise = return ()

-- | 'mmLoggerInfo' prints which calls are happening and which
--   endpoints are being hit, but without the payloads.
mmLoggerInfo :: Handle -> Logger
mmLoggerInfo h LogEvent { logFunction = f, logEventType = e } = do
  now <- getCurrentTime
  canSeek <- hIsSeekable h
  when canSeek $ hSeek h SeekFromEnd 0
  mapM_ (hPutStr h)
    [ "[", show now, "] ", f, ": ", info e, "\n" ]
  hFlush h
  where info (HttpRequest m s _) = show m ++ " " ++ s
        info (HttpResponse n s _) = show n ++ " from " ++ s
        info (WebSocketRequest _) = "websocket request"
        info (WebSocketResponse _) = "websocket request"
        info WebSocketPing = "websocket ping"
        info WebSocketPong = "websocket pong"

-- | 'mmLoggerInfoErr' prints request/response data without payloads
--   to 'stderr'
mmLoggerInfoErr :: Logger
mmLoggerInfoErr = mmLoggerInfo stderr

-- | 'mmLoggerInfoFilterErr' takes a user-defined predicate that
--   it uses to select which events to log before logging them to
--   'stderr'.
mmLoggerInfoFilterErr :: (LogEvent -> Bool) -> Logger
mmLoggerInfoFilterErr p l = mmLoggerInfoFilter p stderr l