{-# language GeneralizedNewtypeDeriving #-}

module Prometheus.Metric (
    Metric (..)
,   Sample (..)
,   SampleGroup (..)
,   SampleType (..)
) where

import Prometheus.Info
import Prometheus.Label

import Control.DeepSeq
import qualified Data.ByteString as BS
import Data.Text (Text)


-- | The type of a sample. This corresponds to the 5 types of metrics supported
-- by Prometheus.
data SampleType
    = CounterType
    | GaugeType
    | SummaryType
    | HistogramType
    | UntypedType

instance Show SampleType where
    show CounterType   = "counter"
    show GaugeType     = "gauge"
    show SummaryType   = "summary"
    show HistogramType = "histogram"
    show UntypedType   = "untyped"

-- | A single value recorded at a moment in time. The sample type contains the
-- name of the sample, a list of labels and their values, and the value encoded
-- as a ByteString.
data Sample = Sample Text LabelPairs BS.ByteString
    deriving (Show)

-- | A Sample group is a list of samples that is tagged with meta data
-- including the name, help string, and type of the sample.
data SampleGroup = SampleGroup Info SampleType [Sample]
    deriving (Show)

-- | A metric represents a single value that is being monitored. It is comprised
-- of a handle value and a collect method. The handle value is typically a new
-- type wrapped value that provides access to the internal state of the metric.
-- The collect method samples the current value of the metric.
newtype Metric s =
  Metric
    { -- | 'construct' is an 'IO' action that creates a new instance of a metric.
      -- For example, in a counter, this 'IO' action would create a mutable reference
      -- to maintain the state of the counter.
      --
      -- 'construct' returns two things:
      --
      -- 1. The state of the metric itself, which can be used to modify the
      --    metric. A counter would return state pointing to the mutable
      --    reference.
      -- 2. An 'IO' action that samples the metric and returns 'SampleGroup's.
      --    This is the data that will be stored by Prometheus. 
      construct :: IO (s, IO [SampleGroup])
    }

instance NFData a => NFData (Metric a) where
  rnf (Metric a) = a `seq` ()