module Ratel.Wai ( ratelMiddleware, toRequest ) where

import qualified Control.Concurrent as Concurrent
import qualified Control.Exception as Exception
import qualified Data.ByteString.Char8 as BS
import qualified Data.CaseInsensitive as CI
import qualified Data.Char as Char
import qualified Data.Map as Map
import qualified Network.HTTP.Client as Client
import qualified Network.Wai as Wai
import qualified Ratel


ratelMiddleware :: Ratel.ApiKey -> Maybe Client.Manager -> (Ratel.Payload -> IO Ratel.Payload) -> Wai.Middleware
ratelMiddleware apiKey maybeManager modify handle request respond = do
    Exception.catch
        (do
            handle request (\ response -> do
                respond response))
        (\ exception -> do
            let err = Ratel.toError (exception :: Exception.SomeException)
            let req = toRequest request
            let server = Ratel.Server
                    { Ratel.serverEnvironmentName = Nothing
                    , Ratel.serverHostname = Nothing
                    , Ratel.serverProjectRoot = Nothing
                    }
            payload <- modify Ratel.Payload
                    { Ratel.payloadError = err
                    , Ratel.payloadNotifier = Nothing
                    , Ratel.payloadRequest = Just req
                    , Ratel.payloadServer = server
                    }
            _ <- Concurrent.forkIO (do
                _ <- Ratel.notify apiKey maybeManager payload
                return ())
            Exception.throwIO exception)


toRequest :: Wai.Request -> Ratel.Request
toRequest request =
    Ratel.Request
        { Ratel.requestAction = Nothing
        , Ratel.requestCgiData = Just (Map.union
            (Map.fromList
                [ ("REMOTE_ADDR", show (Wai.remoteHost request))
                , ("REQUEST_METHOD", BS.unpack (Wai.requestMethod request))
                ])
            (Map.fromList (map (\ (k, v) -> ("HTTP_" ++ map (\ c -> if c == '-' then '_' else Char.toUpper c) (BS.unpack (CI.foldedCase k)), BS.unpack v)) (Wai.requestHeaders request))))
        , Ratel.requestComponent = Nothing
        , Ratel.requestContext = Nothing
        , Ratel.requestParams = Just (Map.fromList (map (\ (k, v) -> (BS.unpack k, maybe "" BS.unpack v)) (Wai.queryString request)))
        , Ratel.requestSession = Nothing
        , Ratel.requestUrl = Just (BS.unpack (Wai.rawPathInfo request) ++ BS.unpack (Wai.rawQueryString request))
        }