{-# LANGUAGE RecordWildCards #-}

-- 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 Wire.ConversationSubsystem.One2One
  ( one2OneConvId,
    internalUpsertOne2OneConversation,
  )
where

import Data.Id
import Data.Qualified
import Galley.Types.Conversations.One2One (one2OneConvId)
import Imports
import Polysemy
import Wire.API.Conversation hiding (Member)
import Wire.API.Conversation.Role
import Wire.API.Routes.Internal.Galley.ConversationsIntra
import Wire.API.User
import Wire.ConversationStore
import Wire.StoredConversation
import Wire.UserList

newConnectConversationWithRemote ::
  Local UserId ->
  UserList UserId ->
  NewConversation
newConnectConversationWithRemote creator users =
  NewConversation
    { metadata =
        (defConversationMetadata (Just (tUnqualified creator)))
          { cnvmType = One2OneConv
          },
      users = fmap toUserRole users,
      protocol = BaseProtocolProteusTag,
      groupId = Nothing
    }

internalUpsertOne2OneConversation ::
  forall r.
  (Member ConversationStore r) =>
  UpsertOne2OneConversationRequest ->
  Sem r ()
internalUpsertOne2OneConversation UpsertOne2OneConversationRequest {..} = do
  let dolocal :: Local ConvId -> Sem r ()
      dolocal lconvId = do
        mbConv <- getConversation (tUnqualified lconvId)
        case mbConv of
          Nothing -> do
            let members =
                  case (uooActor, uooActorDesiredMembership) of
                    (LocalActor, Included) -> ulFromLocals [tUnqualified uooLocalUser]
                    (LocalActor, Excluded) -> mempty
                    (RemoteActor, Included) -> ulFromRemotes [uooRemoteUser]
                    (RemoteActor, Excluded) -> mempty
            unless (null members) . void $
              upsertConversation
                lconvId
                (newConnectConversationWithRemote uooLocalUser members)
          Just conv -> do
            case (uooActor, uooActorDesiredMembership) of
              (LocalActor, Included) -> do
                void $ upsertMember lconvId uooLocalUser
                unless (null conv.remoteMembers) $
                  acceptConnectConversation (tUnqualified lconvId)
              (LocalActor, Excluded) -> do
                deleteMembers
                  (tUnqualified lconvId)
                  (UserList [tUnqualified uooLocalUser] [])
              (RemoteActor, Included) -> do
                void $ upsertMembers (tUnqualified lconvId) (UserList [] [(,roleNameWireAdmin) <$> uooRemoteUser])
                unless (null conv.localMembers) $
                  acceptConnectConversation (tUnqualified lconvId)
              (RemoteActor, Excluded) ->
                deleteMembers
                  (tUnqualified lconvId)
                  (UserList [] [uooRemoteUser])
      doremote :: Remote ConvId -> Sem r ()
      doremote rconvId =
        case (uooActor, uooActorDesiredMembership) of
          (LocalActor, Included) -> do
            upsertMembersInRemoteConversation rconvId [tUnqualified uooLocalUser]
          (LocalActor, Excluded) -> do
            deleteMembersInRemoteConversation rconvId [tUnqualified uooLocalUser]
          (RemoteActor, _) -> pure ()

  foldQualified uooLocalUser dolocal doremote uooConvId
