content: {-# LANGUAGE BangPatterns #-}
  {-# LANGUAGE CPP #-}
  module Telegram.Bot.Simple.Debug where
  
  import           Control.Monad.Trans        (liftIO)
  import           Control.Monad.Writer       (tell)
  import           Data.Aeson                 (ToJSON)
  import qualified Data.Aeson.Encode.Pretty   as Aeson
  #if defined(MIN_VERSION_GLASGOW_HASKELL)
  #if MIN_VERSION_GLASGOW_HASKELL(8,6,2,0)
  #else
  import           Data.Monoid                     ((<>))
  #endif
  #endif
  import qualified Data.Text.Lazy             as Text
  import qualified Data.Text.Lazy.Encoding    as Text
  import           Debug.Trace                (trace)
  import           Text.Show.Pretty           (ppShow)
  
  import qualified Telegram.Bot.API           as Telegram
  import           Telegram.Bot.Simple.BotApp
  import           Telegram.Bot.Simple.Eff
  
  -- * Bot debug tracing
  
  -- | This a default bot tracing modifier that relies on
  --
  -- * 'traceTelegramUpdatesJSON'
  -- * 'traceBotActionsShow'
  -- * 'traceBotModelShow'
  traceBotDefault
    :: (Show model, Show action)
    => BotApp model action
    -> BotApp model action
  traceBotDefault
    = traceTelegramUpdatesJSON
    . traceBotActionsShow
    . traceBotModelShow
  
  -- ** Trace 'Telegram.Update's
  
  -- | Trace (debug print) every 'Telegram.Update' before parsing it.
  traceTelegramUpdatesWith
    :: (Telegram.Update -> String)    -- ^ How to display an update.
    -> BotApp model action
    -> BotApp model action
  traceTelegramUpdatesWith f botApp = botApp
    { botAction = \update -> botAction botApp $! trace (f update) update
    }
  
  -- | Trace (debug print) every update as pretty JSON value.
  traceTelegramUpdatesJSON :: BotApp model action -> BotApp model action
  traceTelegramUpdatesJSON = traceTelegramUpdatesWith ppAsJSON
  
  -- | Trace (debug print) every update using 'Show' instance.
  traceTelegramUpdatesShow :: BotApp model action -> BotApp model action
  traceTelegramUpdatesShow = traceTelegramUpdatesWith ppShow
  
  -- ** Trace bot actions
  
  -- | A type of an action to trace.
  data TracedAction action
    = TracedIncomingAction action  -- ^ An action that's about to be handled.
    | TracedIssuedAction action    -- ^ An action that's just been issued by some handler.
    deriving (Eq, Show)
  
  -- | Pretty print 'TraceActionType'.
  ppTracedAction :: Show action => TracedAction action -> String
  ppTracedAction (TracedIncomingAction action) = "Incoming: " <> ppShow action
  ppTracedAction (TracedIssuedAction   action) = "Issued:   " <> ppShow action
  
  -- | Trace (debug print) every incoming and issued action.
  traceBotActionsWith
    :: (TracedAction action -> String)  -- ^ How to display an action.
    -> BotApp model action
    -> BotApp model action
  traceBotActionsWith f botApp = botApp { botHandler = newHandler }
    where
      traceAction (Just action) = Just action <$ do
        liftIO $ putStrLn (f (TracedIssuedAction action))
      traceAction Nothing = pure Nothing
      
      newHandler !action model = do
        Eff (tell (map (>>= traceAction) actions))
        pure newModel
        where
          (newModel, actions) = runEff $
            botHandler botApp
              (trace (f (TracedIncomingAction action)) action)
              model
  
  -- | Trace (debug print) bot actions using 'Show' instance.
  traceBotActionsShow
    :: Show action => BotApp model action -> BotApp model action
  traceBotActionsShow = traceBotActionsWith ppTracedAction
  
  -- ** Trace bot state model
  
  -- | Trace (debug print) bot model.
  traceBotModelWith
    :: (model -> String)    -- ^ How to display a model.
    -> BotApp model action
    -> BotApp model action
  traceBotModelWith f botApp = botApp
    { botInitialModel = newInitialModel
    , botHandler = newHandler
    }
      where
        !newInitialModel = traceModel (botInitialModel botApp)
        newHandler action !model = traceModel <$> botHandler botApp action model
        traceModel = trace <$> f <*> id
  
  -- | Trace (debug print) bot model using 'Show' instance.
  traceBotModelShow
    :: Show model => BotApp model action -> BotApp model action
  traceBotModelShow = traceBotModelWith ppShow
  
  -- | Trace (debug print) bot model using 'Show' instance.
  traceBotModelJSON
    :: ToJSON model => BotApp model action -> BotApp model action
  traceBotModelJSON = traceBotModelWith ppAsJSON
  
  -- * Helpers
  
  -- | Pretty print a value as JSON.
  ppAsJSON :: ToJSON a => a -> String
  ppAsJSON = Text.unpack . Text.decodeUtf8 . Aeson.encodePretty
  