{-# LANGUAGE OverloadedStrings #-}

module System.Metrics.Prometheus.Http.Push
       ( pushMetrics
       , parseURI
       )
       where

import           Control.Concurrent                    (threadDelay)
import           Control.Monad                         (forever)
import           Data.ByteString.Builder               (toLazyByteString)
import           Data.Map                              (foldMapWithKey)
import           Data.Text                             (Text, unpack)
import           Network.HTTP.Client                   (Request (..),
                                                        RequestBody (..),
                                                        getUri,
                                                        httpNoBody,
                                                        parseRequest,
                                                        requestBody,
                                                        requestFromURI,
                                                        requestHeaders)
import           Network.HTTP.Types                    (hContentType, methodPut)
import           Network.HTTP.Client.TLS               (newTlsManager)
import           Network.URI                           (URI (..), URIAuth,
                                                        nullURI)

import           System.Metrics.Prometheus.Encode.Text (encodeMetrics)
import           System.Metrics.Prometheus.MetricId    (Labels (..))
import           System.Metrics.Prometheus.Registry    (RegistrySample)

-- | Parses a uri such that
-- @
--   parseURI "https://example.com"
--      ===
--   Just (URI "https:" "//example.com"
-- @
parseURI :: String -> Maybe URI
parseURI = fmap getUri . parseRequest

pushMetrics :: URI               -- ^ PushGateway URI name, including port number (ex: @parseUri https://myGateway.com:8080@)
            -> Text              -- ^ Job name
            -> Labels            -- ^ Label set to use as a grouping key for metrics
            -> Int               -- ^ Microsecond push frequency
            -> IO RegistrySample -- ^ Action to get latest metrics
            -> IO ()
pushMetrics gatewayURI jobName labels frequencyMicros getSample = do
    manager    <- newTlsManager
    gn         <- maybe (error "Invalid URI Authority") pure gatewayName
    requestUri <- requestFromURI $ buildUri scheme gn jobName labels
    forever $ getSample >>= flip httpNoBody manager . request requestUri >> threadDelay frequencyMicros
  where
    URI scheme gatewayName _ _ _ = gatewayURI
    request req sample = req
        { method         = methodPut
        , requestBody    = RequestBodyLBS . toLazyByteString $ encodeMetrics sample
        , requestHeaders = [(hContentType, "text/plain; version=0.0.4")]
        }

buildUri :: String -> URIAuth -> Text -> Labels -> URI
buildUri scheme gatewayName jobName (Labels ls) = nullURI
    { uriScheme    = scheme
    , uriAuthority = Just gatewayName
    , uriPath      = "/metrics/job/" ++ unpack jobName ++ foldMapWithKey labelPath ls
    }
  where labelPath k v = "/" ++ unpack k ++ "/" ++ unpack v