module Ribosome.Host.Data.RpcMessage where

import Data.MessagePack (Object (ObjectArray, ObjectNil))
import qualified Data.Serialize as Serialize
import Data.Serialize (Serialize)
import Exon (exon)

import Ribosome.Host.Class.Msgpack.Array (MsgpackArray (msgpackArray))
import Ribosome.Host.Class.Msgpack.Decode (pattern Msgpack, MsgpackDecode (fromMsgpack))
import Ribosome.Host.Class.Msgpack.Encode (MsgpackEncode (toMsgpack))
import Ribosome.Host.Class.Msgpack.Error (decodeError)
import qualified Ribosome.Host.Data.Request as Request
import Ribosome.Host.Data.Request (Request, TrackedRequest (TrackedRequest), formatReq, formatTrackedReq)
import qualified Ribosome.Host.Data.Response as Response
import Ribosome.Host.Data.Response (TrackedResponse (TrackedResponse), formatTrackedResponse)

rpcError :: Object -> Text
rpcError = \case
  Msgpack e ->
    e
  ObjectArray [_, Msgpack e] ->
    e
  o ->
    show o

pattern ErrorPayload :: Text -> Object
pattern ErrorPayload e <- (rpcError -> e)

data RpcMessage =
  Request TrackedRequest
  |
  Response TrackedResponse
  |
  Notification Request
  deriving stock (Eq, Show)

instance MsgpackEncode RpcMessage where
  toMsgpack = \case
    Request (TrackedRequest i (Request.Request method payload)) ->
      msgpackArray (0 :: Int) i method payload
    Response (TrackedResponse i (Response.Success payload)) ->
      msgpackArray (1 :: Int) i () payload
    Response (TrackedResponse i (Response.Error payload)) ->
      msgpackArray (1 :: Int) i payload ()
    Notification (Request.Request method payload) ->
      msgpackArray (2 :: Int) method payload

instance MsgpackDecode RpcMessage where
  fromMsgpack = \case
    ObjectArray [Msgpack (0 :: Int), Msgpack i, Msgpack method, Msgpack payload] ->
      Right (Request (TrackedRequest i (Request.Request method payload)))
    ObjectArray [Msgpack (1 :: Int), Msgpack i, ObjectNil, payload] ->
      Right (Response (TrackedResponse i (Response.Success payload)))
    ObjectArray [Msgpack (1 :: Int), Msgpack i, ErrorPayload e, ObjectNil] ->
      Right (Response (TrackedResponse i (Response.Error e)))
    ObjectArray [Msgpack (2 :: Int), Msgpack method, Msgpack payload] ->
      Right (Notification (Request.Request method payload))
    o ->
      decodeError [exon|Invalid format for RpcMessage: #{show o}|]

instance Serialize RpcMessage where
  put =
    Serialize.put . toMsgpack
  get =
    either (fail . show) pure . fromMsgpack =<< Serialize.get

formatRpcMsg :: RpcMessage -> Text
formatRpcMsg = \case
  Request req ->
    [exon|request #{formatTrackedReq req}|]
  Response res ->
    [exon|response #{formatTrackedResponse res}|]
  Notification req ->
    [exon|notification #{formatReq req}|]

module Ribosome.Host.Data.RpcMessage where

import Data.MessagePack (Object (ObjectArray, ObjectNil))
import qualified Data.Serialize as Serialize
import Data.Serialize (Serialize)
import Exon (exon)

import Ribosome.Host.Class.Msgpack.Array (MsgpackArray (msgpackArray))
import Ribosome.Host.Class.Msgpack.Decode (pattern Msgpack, MsgpackDecode (fromMsgpack))
import Ribosome.Host.Class.Msgpack.Encode (MsgpackEncode (toMsgpack))
import Ribosome.Host.Class.Msgpack.Error (decodeError)
import qualified Ribosome.Host.Data.Request as Request
import Ribosome.Host.Data.Request (Request, TrackedRequest (TrackedRequest), formatReq, formatTrackedReq)
import qualified Ribosome.Host.Data.Response as Response
import Ribosome.Host.Data.Response (TrackedResponse (TrackedResponse), formatTrackedResponse)

rpcError :: Object -> Text
rpcError = \case
  Msgpack e ->
    e
  ObjectArray [_, Msgpack e] ->
    e
  o ->
    show o

pattern ErrorPayload :: Text -> Object
pattern ErrorPayload e <- (rpcError -> e)

data RpcMessage =
  Request TrackedRequest
  |
  Response TrackedResponse
  |
  Notification Request
  deriving stock (Eq, Show)

instance MsgpackEncode RpcMessage where
  toMsgpack = \case
    Request (TrackedRequest i (Request.Request method payload)) ->
      msgpackArray (0 :: Int) i method payload
    Response (TrackedResponse i (Response.Success payload)) ->
      msgpackArray (1 :: Int) i () payload
    Response (TrackedResponse i (Response.Error payload)) ->
      msgpackArray (1 :: Int) i payload ()
    Notification (Request.Request method payload) ->
      msgpackArray (2 :: Int) method payload

instance MsgpackDecode RpcMessage where
  fromMsgpack = \case
    ObjectArray [Msgpack (0 :: Int), Msgpack i, Msgpack method, Msgpack payload] ->
      Right (Request (TrackedRequest i (Request.Request method payload)))
    ObjectArray [Msgpack (1 :: Int), Msgpack i, ObjectNil, payload] ->
      Right (Response (TrackedResponse i (Response.Success payload)))
    ObjectArray [Msgpack (1 :: Int), Msgpack i, ErrorPayload e, ObjectNil] ->
      Right (Response (TrackedResponse i (Response.Error e)))
    ObjectArray [Msgpack (2 :: Int), Msgpack method, Msgpack payload] ->
      Right (Notification (Request.Request method payload))
    o ->
      decodeError [exon|Invalid format for RpcMessage: #{show o}|]

instance Serialize RpcMessage where
  put =
    Serialize.put . toMsgpack
  get =
    either (fail . show) pure . fromMsgpack =<< Serialize.get

formatRpcMsg :: RpcMessage -> Text
formatRpcMsg = \case
  Request req ->
    [exon|request #{formatTrackedReq req}|]
  Response res ->
    [exon|response #{formatTrackedResponse res}|]
  Notification req ->
    [exon|notification #{formatReq req}|]
