{-# LANGUAGE OverloadedStrings #-}

module Servant.Client.Core.ServerSentEventsSpec (spec) where

import Control.Monad.Trans.Except (runExceptT)
import qualified Data.ByteString.Lazy as ByteString
import Data.Foldable (for_)
import Data.Int (Int64)
import Servant.API.ContentTypes (EventStreamChunk (..))
import Servant.API.Stream (FromSourceIO (fromSourceIO))
import Servant.Types.SourceT (runSourceT, source)
import Test.Hspec (Spec, describe, it, shouldBe)

import Servant.Client.Core.ServerSentEvents
  ( Event (..)
  , EventIgnoreReason (EventComment)
  , EventMessage (..)
  , unEventMessageStreamT
  , unEventStreamT
  )

spec :: Spec
spec = describe "Servant.Client.Core.ServerSentEvent" $ do
  describe "EventMessageStreamT" $ do
    it "processes chunks correctly" $ do
      let allMessages =
            ByteString.intercalate
              "\n"
              [ "retry: 30"
              , "data: Hello World"
              , "id: 1"
              , ""
              , "event: my_event"
              , "data"
              , "id: 2"
              , ":Just a comment"
              , ""
              , "data: Bye"
              ]

      for_ [1, 10, 100] $ \chunkSize -> do
        src <-
          fromSourceIO $
            source $
              map EventStreamChunk $
                chunkify chunkSize allMessages

        result <-
          runExceptT $
            runSourceT $
              unEventMessageStreamT src

        result
          `shouldBe` Right
            [ EventRetry 30
            , EventData "Hello World"
            , EventSetLastId "1"
            , EventDispatch
            , EventSetName "my_event"
            , EventData ""
            , EventSetLastId "2"
            , EventIgnore (EventComment "Just a comment")
            , EventDispatch
            , EventData "Bye"
            , EventDispatch
            ]

  describe "EventStreamT" $ do
    it "processes chunks correctly" $ do
      let allMessages =
            ByteString.intercalate
              "\n"
              [ "retry: 30"
              , "data: Hello World"
              , "id: 1"
              , ""
              , "event: my_event"
              , "data"
              , "id: 2"
              , ":Just a comment"
              , ""
              , "data: Bye"
              ]

      for_ [1, 10, 100] $ \chunkSize -> do
        src <-
          fromSourceIO $
            source $
              map EventStreamChunk $
                chunkify chunkSize allMessages
        result <-
          runExceptT $
            runSourceT $
              unEventStreamT src

        result
          `shouldBe` Right
            [ Event Nothing "Hello World"
            , Event (Just "my_event") ""
            , Event Nothing "Bye"
            ]

chunkify :: Int64 -> ByteString.ByteString -> [ByteString.ByteString]
chunkify chunkSize input =
  if ByteString.null input
    then
      []
    else
      let (h, t) = ByteString.splitAt chunkSize input
       in h : chunkify chunkSize t
