content: {-# LANGUAGE LambdaCase #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module OpenTelemetry.Propagator.W3CIntegrationSpec (spec) where
  
  import Data.ByteString (ByteString)
  import qualified Data.ByteString.Char8 as C8
  import qualified Data.Text as T
  import qualified Data.Text.Encoding as TE
  import OpenTelemetry.Common (TraceFlags (..))
  import OpenTelemetry.Propagator.W3CTraceContext
  import OpenTelemetry.Trace.Core
  import OpenTelemetry.Trace.Id
  import OpenTelemetry.Trace.TraceState (Key (..), Value (..), empty, fromList, insert, toList)
  import Test.Hspec
  
  
  spec :: Spec
  spec = describe "W3C TraceContext Integration" $ do
    describe "decodeSpanContext" $ do
      it "decodes traceparent and empty tracestate" $ do
        let traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
            tracestate = Nothing
            result = decodeSpanContext traceparent tracestate
        case result of
          Just spanCtx -> do
            OpenTelemetry.Trace.Core.traceFlags spanCtx `shouldBe` TraceFlags 1
            traceState spanCtx `shouldBe` empty
          Nothing -> expectationFailure "Failed to decode span context"
  
      it "decodes traceparent and tracestate with single entry" $ do
        let traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
            tracestate = Just "vendor1=value1"
            result = decodeSpanContext traceparent tracestate
        case result of
          Just spanCtx -> do
            OpenTelemetry.Trace.Core.traceFlags spanCtx `shouldBe` TraceFlags 1
            traceState spanCtx `shouldBe` insert (Key "vendor1") (Value "value1") empty
          Nothing -> expectationFailure "Failed to decode span context"
  
      it "decodes traceparent and tracestate with multiple entries" $ do
        let traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
            tracestate = Just "vendor1=value1,vendor2=value2,vendor3=value3"
            result = decodeSpanContext traceparent tracestate
        case result of
          Just spanCtx -> do
            OpenTelemetry.Trace.Core.traceFlags spanCtx `shouldBe` TraceFlags 1
            let decodedPairs = toList (traceState spanCtx)
            decodedPairs `shouldContain` [(Key "vendor1", Value "value1")]
            decodedPairs `shouldContain` [(Key "vendor2", Value "value2")]
            decodedPairs `shouldContain` [(Key "vendor3", Value "value3")]
            length decodedPairs `shouldBe` 3
          Nothing -> expectationFailure "Failed to decode span context"
  
      it "handles invalid tracestate gracefully" $ do
        let traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
            tracestate = Just "INVALID$KEY=value1" -- Invalid key format
            result = decodeSpanContext traceparent tracestate
        case result of
          Just spanCtx -> do
            OpenTelemetry.Trace.Core.traceFlags spanCtx `shouldBe` TraceFlags 1
            traceState spanCtx `shouldBe` empty -- Should fall back to empty
          Nothing -> expectationFailure "Failed to decode span context"
  
      it "returns Nothing for invalid traceparent" $ do
        let traceparent = Just "invalid-traceparent"
            tracestate = Just "vendor1=value1"
            result = decodeSpanContext traceparent tracestate
        result `shouldBe` Nothing
  
      it "returns Nothing for missing traceparent" $ do
        let traceparent = Nothing
            tracestate = Just "vendor1=value1"
            result = decodeSpanContext traceparent tracestate
        result `shouldBe` Nothing
  
    describe "encodeTraceState integration" $ do
      it "encodes complex tracestate correctly" $ do
        let complexState =
              fromList
                [ (Key "tenant@vendor", Value "complex-value_with*chars")
                , (Key "simple", Value "value")
                , (Key "numeric123", Value "123-456")
                ]
            encoded = encodeTraceState complexState
        -- Verify it can be round-tripped
        let traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
            result = decodeSpanContext traceparent (Just encoded)
        case result of
          Just spanCtx -> do
            let decodedState = traceState spanCtx
            decodedState `shouldBe` complexState
          Nothing -> expectationFailure "Failed to round-trip complex tracestate"
  
      it "handles empty tracestate in round-trip" $ do
        let encoded = encodeTraceState empty
        encoded `shouldBe` ""
        let traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
            result = decodeSpanContext traceparent (if encoded == "" then Nothing else Just encoded)
        case result of
          Just spanCtx -> traceState spanCtx `shouldBe` empty
          Nothing -> expectationFailure "Failed to handle empty tracestate"
  
    describe "W3C specification compliance" $ do
      it "respects 32 entry limit in parsing" $ do
        let entries = ["key" ++ show i ++ "=value" ++ show i | i <- [1 .. 40]]
            longTracestate = C8.intercalate "," $ map C8.pack entries
            traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
            result = decodeSpanContext traceparent (Just longTracestate)
        case result of
          Just spanCtx -> do
            let stateEntries = traceState spanCtx
            length (toList stateEntries) `shouldBe` 32
          Nothing -> expectationFailure "Failed to parse long tracestate"
  
      it "validates key format according to spec" $ do
        let testCases =
              [ ("validkey", True)
              , ("valid123", True)
              , ("valid_key", True)
              , ("valid-key", True)
              , ("valid*key", True)
              , ("valid/key", True)
              , ("tenant@vendor", True)
              , ("123numeric", True)
              , ("INVALIDKEY", False) -- Must start with lowercase
              , ("invalid$key", False) -- Invalid character
              , ("", False) -- Empty key
              ]
        mapM_
          ( \(key, shouldSucceed) -> do
              let tracestate = C8.pack $ T.unpack key ++ "=value"
                  traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
                  result = decodeSpanContext traceparent (Just tracestate)
              if shouldSucceed
                then
                  result
                    `shouldSatisfy` ( \case
                                        Just spanCtx -> not $ null $ toList $ traceState spanCtx
                                        Nothing -> False
                                    )
                else
                  result
                    `shouldSatisfy` ( \case
                                        Just spanCtx -> null $ toList $ traceState spanCtx
                                        Nothing -> True
                                    )
          )
          testCases
  
      it "validates value format according to spec" $ do
        let testCases =
              [ ("validvalue", True)
              , ("valid value with spaces", True)
              , ("valid-value_with*special/chars", True)
              , ("valid!value#with$symbols%", True)
              , ("value,with,comma", False) -- Comma not allowed
              , ("value=with=equals", False) -- Equals not allowed in continuation
              ]
        mapM_
          ( \(value, shouldSucceed) -> do
              let tracestate = C8.pack $ "validkey=" ++ T.unpack value
                  traceparent = Just "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
                  result = decodeSpanContext traceparent (Just tracestate)
              if shouldSucceed
                then
                  result
                    `shouldSatisfy` ( \case
                                        Just spanCtx -> not $ null $ toList $ traceState spanCtx
                                        Nothing -> False
                                    )
                else
                  result
                    `shouldSatisfy` ( \case
                                        Just spanCtx -> null $ toList $ traceState spanCtx
                                        Nothing -> True
                                    )
          )
          testCases
  