-- | Emulation of HapiJS Boom responses.

module Network.Oz.Boom
  ( badRequest
  , unauthorized
  , forbidden
  , internal
  , errHandler
  ) where

import           Control.Monad.IO.Class    (MonadIO (..), liftIO)
import           Data.Aeson                (Value (..), object, (.=))
import           Data.Maybe                (fromMaybe)
import           Data.Monoid               ((<>))
import           Data.Text.Encoding        (decodeUtf8)
import           Data.Text.Lazy            (Text, pack, toStrict)
import qualified Data.Text.Lazy            as TL
import           Network.HTTP.Types.Status (Status (..), mkStatus, status400,
                                            status401, status403, status500)
import           Text.Read                 (readMaybe)
import           Web.Scotty.Trans

-- fixme: can't figure out scotty custom error types, so am doing lame
-- conversion of status codes to strings.
{-
import Control.Monad.Error

instance Error Boom where
  strMsg = Boom status500

data Boom = Boom Status String
  deriving (Show, Eq)

instance ScottyError Boom where
  stringError = Boom status500
  showError = TL.pack . show
-}

type Boom = Text

badRequest :: Monad m => String -> ActionT Boom m a
badRequest e = boom status400 e

unauthorized :: Monad m => String -> ActionT Boom m a
unauthorized e = boom status401 e

forbidden :: Monad m => String -> ActionT Boom m a
forbidden e = boom status403 e

internal :: Monad m => String -> ActionT Boom m a
internal e = boom status500 e

boom :: Monad m => Status -> String -> ActionT Boom m a
boom code e = do
  status code
  let e' = pack e
  json $ boomObject code e'
  raise $ pack (show (statusCode code)) <> " " <> e'
  -- raise (Boom code e)

boomObject :: Status -> Text -> Value
boomObject code msg = object
  [ "statusCode" .= statusCode code
  , "error" .= decodeUtf8 (statusMessage code)
  , "message" .= msg
  ]

{-
errHandlerBoom :: Monad m => Boom -> ActionT Boom m ()
errHandlerBoom (Boom code msg) = status code
-}

errHandler :: MonadIO m => Text -> ActionT Text m ()
errHandler e = do
  liftIO $ putStrLn $ "*** errHandler " ++ show e
  let (code, msg) = TL.break (== ' ') e
  status $ maybe status500 toEnum (readMaybe (TL.unpack code))
