-- 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 Util.Invitation
  ( headInvitation404,
    getInvitation,
    getInvitationCode,
    registerInvitation,
    acceptWithName,
  )
where

import Bilge
import Bilge.Assert ((!!!), (<!!), (===))
import Control.Lens
import qualified Data.Aeson as Aeson
import Data.Aeson.Lens (key, _String)
import Data.ByteString.Conversion
import Data.Id
import Data.Text.Encoding (encodeUtf8)
import Imports
import Util
import Wire.API.Team.Invitation (Invitation (..), InvitationUserView (..))
import Wire.API.User

headInvitation404 :: (HasCallStack) => BrigReq -> EmailAddress -> Http ()
headInvitation404 brig email = do
  Bilge.head (brig . path "/teams/invitations/by-email" . contentJson . queryItem "email" (toByteString' email))
    !!! const 404 === statusCode

getInvitation :: (HasCallStack) => BrigReq -> EmailAddress -> Http Invitation
getInvitation brig email =
  responseJsonUnsafe
    <$> Bilge.get
      ( brig
          . path "/i/teams/invitations/by-email"
          . contentJson
          . queryItem "email" (toByteString' email)
          . expect2xx
      )

getInvitationCode ::
  (MonadIO m, MonadHttp m, HasCallStack) =>
  BrigReq ->
  TeamId ->
  InvitationId ->
  m (Maybe InvitationCode)
getInvitationCode brig t ref = do
  r <-
    get
      ( brig
          . path "/i/teams/invitation-code"
          . queryItem "team" (toByteString' t)
          . queryItem "invitation_id" (toByteString' ref)
      )
  let lbs = fromMaybe "" $ responseBody r
  pure $ fromByteString (maybe (error "No code?") encodeUtf8 (lbs ^? key "code" . _String))

registerInvitation :: (HasCallStack) => EmailAddress -> Name -> InvitationCode -> Bool -> TestSpar ()
registerInvitation email name inviteeCode shouldSucceed = do
  env <- ask
  let brig = env ^. teBrig
  infoResp <-
    call $
      get
        ( brig
            . path "/teams/invitations/info"
            . queryItem "code" (toByteString' inviteeCode)
        )
  inviteeName <-
    if statusCode infoResp == 200
      then do
        info :: InvitationUserView <- responseJsonError infoResp
        pure $ fromMaybe name info.invitation.inviteeName
      else pure name
  call $
    void $
      post
        ( brig
            . path "/register"
            . contentJson
            . header "X-Forwarded-For" "127.0.0.42"
            . json (acceptWithName inviteeName email inviteeCode)
        )
        <!! const (if shouldSucceed then 201 else 400) === statusCode

acceptWithName :: Name -> EmailAddress -> InvitationCode -> Aeson.Value
acceptWithName name email code =
  Aeson.object
    [ "name" Aeson..= fromName name,
      "email" Aeson..= fromEmail email,
      "password" Aeson..= defPassword,
      "team_code" Aeson..= code
    ]
