module Handlers (
    apiError
  , appHandler
  ) where

import Control.Applicative
import Control.Exception (SomeException)
import Control.Monad.Base
import Data.Aeson
import Data.ByteString (ByteString)
import Data.Monoid.Utils
import Database.PostgreSQL.PQTypes
import Log
import Network.HTTP.Types
import Network.Wai
import Prelude
import qualified Data.ByteString.Builder as BSB
import qualified Data.Foldable as F
import qualified Data.Vector as V

import SQL

type HandlerM = DBT (LogT IO)
type HandlerRunner = forall r. HandlerM r -> IO r

data HandlerEnv = HandlerEnv {
  heRunHandler :: !HandlerRunner
, heRequest    :: !Request
, heRespond    :: !(Response -> IO ResponseReceived)
}

apiError :: SomeException -> Response
apiError = responseLBS badRequest400 [(hContentType, jsonContentType)] . encode . f
  where
    f err = object ["error" .= show err]

jsonContentType :: ByteString
jsonContentType = "application/json"

----------------------------------------

appHandler :: HandlerRunner -> Application
appHandler runHandler rq respond = case pathInfo rq of
  -- We could use wai-routes for that, but there really is no point
  -- as these routes are very simple and won't get more complicated.
  ["api", "components"] | isGET -> handleApiComponents env
  ["api", "logs"] | isPOST -> handleApiLogs env
  _ -> respond $ responseLBS notFound404 [] "Nothing is here."
  where
    isGET  = requestMethod rq == methodGet
    isPOST = requestMethod rq == methodPost

    env = HandlerEnv {
      heRunHandler = runHandler
    , heRequest = rq
    , heRespond = respond
    }

handleApiComponents :: HandlerEnv -> IO ResponseReceived
handleApiComponents HandlerEnv{..} = do
  components <- heRunHandler $ V.fromList . map String <$> fetchComponents
  heRespond . responseOk . encode $ object ["components" .= Array components]
  where
    responseOk = responseLBS ok200 [(hContentType, jsonContentType)]

handleApiLogs :: HandlerEnv -> IO ResponseReceived
handleApiLogs HandlerEnv{..} = do
  logRq <- parseLogRequest =<< lazyRequestBody heRequest
  heRespond . responseOk $ \write _flush -> heRunHandler $ do
    liftBase $ write "{\"logs\":["
    foldChunkedLogs logRq (\() -> liftBase $ write ",") () $ \() qr -> liftBase $ do
      write . mintercalate (BSB.lazyByteString ",")
            . map (BSB.lazyByteString . encode)
            $ F.toList qr
    liftBase $ write "]}"
  where
    responseOk = responseStream ok200 [(hContentType, jsonContentType)]
