-- | Capability record for the NATS client surface.
module API
  (
    Client (..)
  , MsgView (..)
  , PublishOption
  , SubscribeOption
  , withSubscriptionExpiry
  , withQueueGroup
  , withPayload
  , withReplyCallback
  , withReplyTo
  , withHeaders
  ) where

import qualified Data.ByteString    as BS
import           Data.Time.Clock    (NominalDiffTime)
import           Lib.CallOption     (CallOption)
import           Publish.Config     (PublishConfig)
import           Subscription.Types (SubscribeConfig (..))
import qualified Types.Msg          as Msg
import           Types.Msg          (Headers, Payload, SID, Subject)

-- | Client capabilities for publishing, subscribing, and lifecycle control.
data Client = Client
                { Client -> Subject -> [PublishOption] -> IO ()
publish :: Subject -> [PublishOption] -> IO ()
                  -- ^ Publish a message, optionally overriding publish options.
                , Client
-> Subject
-> [SubscribeOption]
-> (Maybe MsgView -> IO ())
-> IO Subject
subscribe :: Subject -> [SubscribeOption] -> (Maybe MsgView -> IO ()) -> IO SID
                  -- ^ Subscribe to a subject and handle delivered messages.
                , Client
-> Subject
-> [SubscribeOption]
-> (Maybe MsgView -> IO ())
-> IO Subject
request :: Subject -> [SubscribeOption] -> (Maybe MsgView -> IO ()) -> IO SID
                  -- ^ Subscribe with request semantics and auto-unsubscribe after a reply.
                , Client -> Subject -> IO ()
unsubscribe :: SID -> IO ()
                  -- ^ Unsubscribe from a subscription by SID.
                , Client -> IO Subject
newInbox :: IO Subject
                  -- ^ Create a unique inbox subject for replies.
                , Client -> IO () -> IO ()
ping :: IO () -> IO ()
                  -- ^ Send a ping and run the callback when a pong arrives.
                , Client -> IO ()
flush :: IO ()
                  -- ^ Flush buffered writes to the server.
                , Client -> IO ()
reset :: IO ()
                  -- ^ Reset the client connection state.
                , Client -> IO ()
close :: IO ()
                -- ^ Close the client connection and release resources.
                }

-- | MsgView represents a MSG in the NATS protocol.
data MsgView = MsgView
                 { -- | The subject of the message.
                   MsgView -> Subject
subject :: BS.ByteString
                   -- | The SID (subscription ID) of the message.
                 , MsgView -> Subject
sid     :: BS.ByteString
                   -- | The replyTo subject, if any.
                 , MsgView -> Maybe Subject
replyTo :: Maybe BS.ByteString
                   -- | The payload of the message, if any.
                 , MsgView -> Maybe Subject
payload :: Maybe BS.ByteString
                   -- | Headers associated with the message, if any.
                 , MsgView -> Maybe [(Subject, Subject)]
headers :: Maybe [(BS.ByteString, BS.ByteString)]
                 }
  deriving (MsgView -> MsgView -> Bool
(MsgView -> MsgView -> Bool)
-> (MsgView -> MsgView -> Bool) -> Eq MsgView
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MsgView -> MsgView -> Bool
== :: MsgView -> MsgView -> Bool
$c/= :: MsgView -> MsgView -> Bool
/= :: MsgView -> MsgView -> Bool
Eq, Int -> MsgView -> ShowS
[MsgView] -> ShowS
MsgView -> String
(Int -> MsgView -> ShowS)
-> (MsgView -> String) -> ([MsgView] -> ShowS) -> Show MsgView
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MsgView -> ShowS
showsPrec :: Int -> MsgView -> ShowS
$cshow :: MsgView -> String
show :: MsgView -> String
$cshowList :: [MsgView] -> ShowS
showList :: [MsgView] -> ShowS
Show)

type PublishOption = CallOption PublishConfig

type SubscribeOption = CallOption SubscribeConfig

-- | withSubscriptionExpiry sets the reply subscription expiry in seconds.
-- Default: no expiry (reply subscriptions stay open until unsubscribe).
--
-- __Examples:__
--
-- @
-- {-# LANGUAGE OverloadedStrings #-}
--
-- subscribe client \"events.created\" [withSubscriptionExpiry 2] print
-- @
withSubscriptionExpiry :: NominalDiffTime -> SubscribeOption
withSubscriptionExpiry :: NominalDiffTime -> SubscribeOption
withSubscriptionExpiry NominalDiffTime
expirySeconds SubscribeConfig
cfg = SubscribeConfig
cfg { expiry = Just expirySeconds }

-- | withQueueGroup sets the queue group for a subscription.
-- Default: no queue group.
withQueueGroup :: Subject -> SubscribeOption
withQueueGroup :: Subject -> SubscribeOption
withQueueGroup Subject
queueGroup SubscribeConfig
cfg = SubscribeConfig
cfg { subscribeQueueGroup = Just queueGroup }

-- | withPayload is used to set the payload for a publish operation.
-- Default: no payload.
--
-- __Examples:__
--
-- @
-- {-# LANGUAGE OverloadedStrings #-}
--
-- publish client \"updates\" [withPayload \"hello\"]
-- @
withPayload :: Payload -> PublishOption
withPayload :: Subject -> PublishOption
withPayload Subject
payload (Maybe Subject
_, Maybe (Maybe Msg -> IO ())
callback, Maybe [(Subject, Subject)]
headers, Maybe Subject
replyTo') =
  (Subject -> Maybe Subject
forall a. a -> Maybe a
Just Subject
payload, Maybe (Maybe Msg -> IO ())
callback, Maybe [(Subject, Subject)]
headers, Maybe Subject
replyTo')

-- | withReplyCallback is used to set a callback for a reply to a publish operation.
-- Default: no reply subscription; publishes are fire-and-forget.
--
-- __Examples:__
--
-- @
-- {-# LANGUAGE OverloadedStrings #-}
--
-- publish client \"service.echo\" [withReplyCallback print]
-- @
withReplyCallback :: (Maybe MsgView -> IO ()) -> PublishOption
withReplyCallback :: (Maybe MsgView -> IO ()) -> PublishOption
withReplyCallback Maybe MsgView -> IO ()
callback (Maybe Subject
payload, Maybe (Maybe Msg -> IO ())
_, Maybe [(Subject, Subject)]
headers, Maybe Subject
replyTo') =
  (Maybe Subject
payload, (Maybe Msg -> IO ()) -> Maybe (Maybe Msg -> IO ())
forall a. a -> Maybe a
Just (Maybe MsgView -> IO ()
callback (Maybe MsgView -> IO ())
-> (Maybe Msg -> Maybe MsgView) -> Maybe Msg -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Msg -> MsgView) -> Maybe Msg -> Maybe MsgView
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\Msg
msg -> MsgView
    { subject :: Subject
subject = Msg -> Subject
Msg.subject Msg
msg
    , sid :: Subject
sid = Msg -> Subject
Msg.sid Msg
msg
    , replyTo :: Maybe Subject
replyTo = Msg -> Maybe Subject
Msg.replyTo Msg
msg
    , payload :: Maybe Subject
payload = Msg -> Maybe Subject
Msg.payload Msg
msg
    , headers :: Maybe [(Subject, Subject)]
headers = Msg -> Maybe [(Subject, Subject)]
Msg.headers Msg
msg
    })), Maybe [(Subject, Subject)]
headers, Maybe Subject
replyTo')

-- | withReplyTo sets an explicit reply subject for a publish operation.
-- Default: no reply subject unless a reply callback is configured.
--
-- This option only controls the outgoing reply subject. It does not create a
-- subscription; callers that expect multiple replies should subscribe to the
-- reply subject themselves.
withReplyTo :: Subject -> PublishOption
withReplyTo :: Subject -> PublishOption
withReplyTo Subject
replySubject (Maybe Subject
payload, Maybe (Maybe Msg -> IO ())
callback, Maybe [(Subject, Subject)]
headers, Maybe Subject
_) =
  (Maybe Subject
payload, Maybe (Maybe Msg -> IO ())
callback, Maybe [(Subject, Subject)]
headers, Subject -> Maybe Subject
forall a. a -> Maybe a
Just Subject
replySubject)

-- | withHeaders is used to set headers for a publish operation.
-- Default: no headers.
--
-- __Examples:__
--
-- @
-- {-# LANGUAGE OverloadedStrings #-}
--
-- publish client \"updates\" [withHeaders [(\"source\", \"test\")]]
-- @
withHeaders :: Headers -> PublishOption
withHeaders :: [(Subject, Subject)] -> PublishOption
withHeaders [(Subject, Subject)]
headers (Maybe Subject
payload, Maybe (Maybe Msg -> IO ())
callback, Maybe [(Subject, Subject)]
_, Maybe Subject
replyTo') =
  (Maybe Subject
payload, Maybe (Maybe Msg -> IO ())
callback, [(Subject, Subject)] -> Maybe [(Subject, Subject)]
forall a. a -> Maybe a
Just [(Subject, Subject)]
headers, Maybe Subject
replyTo')