-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- This program is free software: you can redistribute it and/or modify it under
-- the terms of the GNU Affero General Public License as published by the Free
-- Software Foundation, either version 3 of the License, or (at your option) any
-- later version.
--
-- This program is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
-- details.
--
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see <https://www.gnu.org/licenses/>.

module Galley.API.MLS.IncomingMessage
  ( IncomingMessage (..),
    IncomingMessageContent (..),
    IncomingPublicMessageContent (..),
    IncomingBundle (..),
    mkIncomingMessage,
    incomingMessageAuthenticatedContent,
    mkIncomingBundle,
  )
where

import GHC.Records
import Imports
import Wire.API.MLS.AuthenticatedContent
import Wire.API.MLS.Commit
import Wire.API.MLS.CommitBundle
import Wire.API.MLS.Epoch
import Wire.API.MLS.Group
import Wire.API.MLS.GroupInfo
import Wire.API.MLS.Message
import Wire.API.MLS.Serialisation
import Wire.API.MLS.Welcome

data IncomingMessage = IncomingMessage
  { IncomingMessage -> Epoch
epoch :: Epoch,
    IncomingMessage -> GroupId
groupId :: GroupId,
    IncomingMessage -> IncomingMessageContent
content :: IncomingMessageContent,
    IncomingMessage -> RawMLS Message
rawMessage :: RawMLS Message
  }

instance HasField "sender" IncomingMessage (Maybe Sender) where
  getField :: IncomingMessage -> Maybe Sender
getField IncomingMessage
msg = case IncomingMessage
msg.content of
    IncomingMessageContentPublic IncomingPublicMessageContent
pub -> Sender -> Maybe Sender
forall a. a -> Maybe a
Just IncomingPublicMessageContent
pub.sender
    IncomingMessageContent
_ -> Maybe Sender
forall a. Maybe a
Nothing

data IncomingMessageContent
  = IncomingMessageContentPublic IncomingPublicMessageContent
  | IncomingMessageContentPrivate

data IncomingPublicMessageContent = IncomingPublicMessageContent
  { IncomingPublicMessageContent -> Sender
sender :: Sender,
    IncomingPublicMessageContent -> FramedContentData
content :: FramedContentData,
    -- for verification
    IncomingPublicMessageContent -> RawMLS FramedContent
framedContent :: RawMLS FramedContent,
    IncomingPublicMessageContent -> RawMLS FramedContentAuthData
authData :: RawMLS FramedContentAuthData
  }

data IncomingBundle = IncomingBundle
  { IncomingBundle -> Epoch
epoch :: Epoch,
    IncomingBundle -> GroupId
groupId :: GroupId,
    IncomingBundle -> Sender
sender :: Sender,
    IncomingBundle -> RawMLS Commit
commit :: RawMLS Commit,
    IncomingBundle -> RawMLS Message
rawMessage :: RawMLS Message,
    IncomingBundle -> Maybe (RawMLS Welcome)
welcome :: Maybe (RawMLS Welcome),
    IncomingBundle -> RawMLS GroupInfo
groupInfo :: RawMLS GroupInfo,
    IncomingBundle -> ByteString
serialized :: ByteString
  }

mkIncomingMessage :: RawMLS Message -> Maybe IncomingMessage
mkIncomingMessage :: RawMLS Message -> Maybe IncomingMessage
mkIncomingMessage RawMLS Message
msg = case RawMLS Message
msg.value.content of
  MessagePublic PublicMessage
pmsg ->
    IncomingMessage -> Maybe IncomingMessage
forall a. a -> Maybe a
Just
      IncomingMessage
        { $sel:epoch:IncomingMessage :: Epoch
epoch = PublicMessage
pmsg.content.value.epoch,
          $sel:groupId:IncomingMessage :: GroupId
groupId = PublicMessage
pmsg.content.value.groupId,
          $sel:content:IncomingMessage :: IncomingMessageContent
content =
            IncomingPublicMessageContent -> IncomingMessageContent
IncomingMessageContentPublic
              IncomingPublicMessageContent
                { $sel:sender:IncomingPublicMessageContent :: Sender
sender = PublicMessage
pmsg.content.value.sender,
                  $sel:content:IncomingPublicMessageContent :: FramedContentData
content = PublicMessage
pmsg.content.value.content,
                  $sel:framedContent:IncomingPublicMessageContent :: RawMLS FramedContent
framedContent = PublicMessage
pmsg.content,
                  $sel:authData:IncomingPublicMessageContent :: RawMLS FramedContentAuthData
authData = PublicMessage
pmsg.authData
                },
          $sel:rawMessage:IncomingMessage :: RawMLS Message
rawMessage = RawMLS Message
msg
        }
  MessagePrivate RawMLS PrivateMessage
pmsg
    | RawMLS PrivateMessage
pmsg.value.tag FramedContentDataTag -> FramedContentDataTag -> Bool
forall a. Eq a => a -> a -> Bool
== FramedContentDataTag
FramedContentApplicationDataTag ->
        IncomingMessage -> Maybe IncomingMessage
forall a. a -> Maybe a
Just
          IncomingMessage
            { $sel:epoch:IncomingMessage :: Epoch
epoch = RawMLS PrivateMessage
pmsg.value.epoch,
              $sel:groupId:IncomingMessage :: GroupId
groupId = RawMLS PrivateMessage
pmsg.value.groupId,
              $sel:content:IncomingMessage :: IncomingMessageContent
content = IncomingMessageContent
IncomingMessageContentPrivate,
              $sel:rawMessage:IncomingMessage :: RawMLS Message
rawMessage = RawMLS Message
msg
            }
  MessageContent
_ -> Maybe IncomingMessage
forall a. Maybe a
Nothing

incomingMessageAuthenticatedContent :: IncomingPublicMessageContent -> AuthenticatedContent
incomingMessageAuthenticatedContent :: IncomingPublicMessageContent -> AuthenticatedContent
incomingMessageAuthenticatedContent IncomingPublicMessageContent
pmsg =
  AuthenticatedContent
    { $sel:wireFormat:AuthenticatedContent :: WireFormatTag
wireFormat = WireFormatTag
WireFormatPublicTag,
      $sel:content:AuthenticatedContent :: RawMLS FramedContent
content = IncomingPublicMessageContent
pmsg.framedContent,
      $sel:authData:AuthenticatedContent :: RawMLS FramedContentAuthData
authData = IncomingPublicMessageContent
pmsg.authData
    }

mkIncomingBundle :: RawMLS CommitBundle -> Maybe IncomingBundle
mkIncomingBundle :: RawMLS CommitBundle -> Maybe IncomingBundle
mkIncomingBundle RawMLS CommitBundle
bundle = do
  IncomingMessage
imsg <- RawMLS Message -> Maybe IncomingMessage
mkIncomingMessage RawMLS CommitBundle
bundle.value.commitMsg
  IncomingPublicMessageContent
content <- case IncomingMessage
imsg.content of
    IncomingMessageContentPublic IncomingPublicMessageContent
c -> IncomingPublicMessageContent -> Maybe IncomingPublicMessageContent
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure IncomingPublicMessageContent
c
    IncomingMessageContent
_ -> Maybe IncomingPublicMessageContent
forall a. Maybe a
Nothing
  RawMLS Commit
commit <- case IncomingPublicMessageContent
content.content of
    FramedContentCommit RawMLS Commit
c -> RawMLS Commit -> Maybe (RawMLS Commit)
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure RawMLS Commit
c
    FramedContentData
_ -> Maybe (RawMLS Commit)
forall a. Maybe a
Nothing
  IncomingBundle -> Maybe IncomingBundle
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
    IncomingBundle
      { $sel:epoch:IncomingBundle :: Epoch
epoch = IncomingMessage
imsg.epoch,
        $sel:groupId:IncomingBundle :: GroupId
groupId = IncomingMessage
imsg.groupId,
        $sel:sender:IncomingBundle :: Sender
sender = IncomingPublicMessageContent
content.sender,
        $sel:commit:IncomingBundle :: RawMLS Commit
commit = RawMLS Commit
commit,
        $sel:rawMessage:IncomingBundle :: RawMLS Message
rawMessage = RawMLS CommitBundle
bundle.value.commitMsg,
        $sel:welcome:IncomingBundle :: Maybe (RawMLS Welcome)
welcome = RawMLS CommitBundle
bundle.value.welcome,
        $sel:groupInfo:IncomingBundle :: RawMLS GroupInfo
groupInfo = RawMLS CommitBundle
bundle.value.groupInfo,
        $sel:serialized:IncomingBundle :: ByteString
serialized = RawMLS CommitBundle
bundle.raw
      }