{-# LANGUAGE OverloadedStrings #-}

module System.Metrics.Prometheus.Http.Scrape
       ( Path
       , serveMetrics
       , serveMetricsT
       , prometheusApp
       )
       where

import           Control.Applicative                            ((<$>))
import           Control.Monad.IO.Class                         (MonadIO,
                                                                 liftIO)
import           Data.Text                                      (Text)
import           Network.HTTP.Types                             (hContentType,
                                                                 methodGet,
                                                                 status200,
                                                                 status404)
import           Network.Wai                                    (Application,
                                                                 Request,
                                                                 Response,
                                                                 pathInfo,
                                                                 requestMethod,
                                                                 responseBuilder,
                                                                 responseLBS)
import           Network.Wai.Handler.Warp                       (Port, run)

import           System.Metrics.Prometheus.Concurrent.RegistryT (RegistryT,
                                                                 sample)
import           System.Metrics.Prometheus.Encode.Text          (encodeMetrics)
import           System.Metrics.Prometheus.Registry             (RegistrySample)


-- | The HTTP web route on which to serve data
--
-- For example:
--
-- * @http://localhost:9090/metrics@ should use a path of @["metrics"]@.
-- * @http://localhost/@ should use a path of @[]@.
type Path = [Text]


serveMetrics :: MonadIO m => Port -> Path -> IO RegistrySample -> m ()
serveMetrics port path = liftIO . run port . prometheusApp path


serveMetricsT :: MonadIO m => Port -> Path -> RegistryT m ()
serveMetricsT port path = liftIO . serveMetrics port path =<< sample


prometheusApp :: Path -> IO RegistrySample -> Application
prometheusApp path runSample request respond
    | isPrometheusRequest path request = respond =<< prometheusResponse <$> runSample
    | otherwise = respond response404
  where
    prometheusResponse = responseBuilder status200 headers . encodeMetrics
    headers = [(hContentType, "text/plain; version=0.0.4")]


response404 :: Response
response404 = responseLBS status404 header404 body404
  where
    header404 = [(hContentType, "text/plain")]
    body404 = "404"


isPrometheusRequest :: Path -> Request -> Bool
isPrometheusRequest path request = isGet && matchesPath
  where
    matchesPath = pathInfo request == path
    isGet = requestMethod request == methodGet