{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}

module Arbor.Monad.Datadog.MetricSpec
  ( spec
  ) where

import Control.Concurrent
import Control.Exception         (bracket)
import Control.Lens
import Control.Monad
import Control.Monad.IO.Class
import Data.Function
import Data.Generics.Product.Any
import Data.Proxy
import Data.Semigroup            ((<>))

import qualified Arbor.Monad.Datadog.MetricApp as A
import qualified Arbor.Monad.Datadog.UdpServer as UDP
import qualified Arbor.Monad.Metric            as M
import qualified Arbor.Monad.Metric.Datadog    as M
import qualified Control.Concurrent.STM        as STM
import qualified Data.ByteString.Char8         as BS
import qualified Data.Map.Strict               as MAP
import qualified Network.Socket                as S hiding (recv, recvFrom, send, sendTo)
import qualified Network.Socket.ByteString     as S
import qualified System.IO                     as IO

import HaskellWorks.Hspec.Hedgehog
import Hedgehog
import Test.Hspec

{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}
{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}
{-# ANN module ("HLint: redundant bracket"          :: String) #-}

handler :: STM.TVar [BS.ByteString] -> UDP.UdpHandler
handler tMsgs addr msg = do
  STM.atomically $ STM.modifyTVar tMsgs (msg:)
  putStrLn $ "From " ++ show addr ++ ": " ++ show msg

encodeMetrics :: [BS.ByteString] -> [BS.ByteString]
encodeMetrics = (:[]) . mconcat . fmap (<> "\n")

spec :: Spec
spec = describe "Arbor.Monad.MetricSpec" $ do
  it "Metrics library actually sends statsd messages over UDP" $ requireTest $ do
    tMessages <- liftIO $ STM.newTVarIO []
    sock <- liftIO $ UDP.createUdpServer "5555"
    threadId <- liftIO $ forkIO $ UDP.runUdpServer sock (handler tMessages)
    liftIO $ threadDelay 1000000
    -- let counterExpected = "MetricApp.counters:10|c|#stat:test.counter\nMetricApp.test.counter:10|c\n" :: BS.ByteString
    -- let gaugeExpected = "MetricApp.gauge:20.000000|g|#stat:test.gauge\nMetricApp.test.gauge:20.000000|g\n" :: BS.ByteString
    liftIO $ A.runMetricApp $ do
      M.metric (M.counter "test.counter"                                        ) 10
      M.metric (M.gauge   "test.gauge"                                          ) 20
      M.metric (M.gauge   "test.gauge"  & the @"tags" .~ M.tags [("foo", "bar")]) 30
      M.logStats
    liftIO $ threadDelay 3000000
    liftIO $ killThread threadId
    messages :: [BS.ByteString] <- liftIO $ STM.readTVarIO tMessages
    mconcat (BS.lines <$> messages) ===
      [ "MetricApp.test.counter:10|c"
      , "MetricApp.test.gauge:20.000000|g"
      , "MetricApp.test.gauge:30.000000|g|#foo:bar"
      ]