{-# LANGUAGE OverloadedStrings #-}

-- This is an approximate implementation of https://www.w3.org/TR/trace-context

module OpenTelemetry.Propagation where

import Data.Word
import Data.Attoparsec.ByteString.Char8
import qualified Data.ByteString.Char8 as BS
import Data.Char (ord)
import Data.List (find)
import Data.String
import Debug.Trace
import GHC.Generics
import OpenTelemetry.Common
import Text.Printf

data TraceParent = TraceParent Int Int
  deriving (Eq, Show, Generic)

extractSpanContextFromHeaders :: (IsString key, Eq key) => [(key, BS.ByteString)] -> Maybe SpanContext
extractSpanContextFromHeaders headers =
  case find ((== "traceparent") . fst) headers of
    Just (_, value) -> case parseSpanContext value of
      Just c -> Just c
    _ -> Nothing

parseSpanContext :: BS.ByteString -> Maybe SpanContext
parseSpanContext input =
  case BS.split '-' input of
    ["00", (fromHex -> Just tid), (fromHex -> Just sid), _] ->
      Just $ SpanContext (SId sid) (TId tid)
    _ -> Nothing

renderSpanContext :: SpanContext -> BS.ByteString
renderSpanContext (SpanContext (SId sid) (TId tid)) =
  BS.pack $ printf "00-%x-%x-00" tid sid

isLowerHexDigit (ord -> w) = (w >= 48 && w <= 57) || (w >= 97 && w <= 102)

fromHex :: BS.ByteString -> Maybe Word64
fromHex bytes = BS.foldl' go (Just 0) bytes
  where
    go Nothing _ = Nothing
    go (Just !result) (ord -> d) | d >= 48 && d < 58 = Just $ result * 16 + fromIntegral d - 48
    go (Just result) (ord -> d) | d >= 97 && d < 124 = Just $ result * 16 + fromIntegral d - 87
    go _ _ = Nothing