-- 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.API.Routes.Public.Galley.TeamMember where

import Data.Id
import Data.Int
import Data.Range
import Data.Set (Set)
import GHC.Generics
import Generics.SOP qualified as GSOP
import Servant
import Servant.OpenApi.Internal.Orphans ()
import Wire.API.Error
import Wire.API.Error.Galley
import Wire.API.Routes.CSV
import Wire.API.Routes.LowLevelStream
import Wire.API.Routes.MultiVerb
import Wire.API.Routes.Named
import Wire.API.Routes.Public
import Wire.API.Routes.Version
import Wire.API.Team.Collaborator
import Wire.API.Team.Member
import Wire.API.User qualified as User

type TeamMemberAPI =
  Named
    "get-team-members"
    ( Summary "Get team members"
        :> CanThrow 'NotATeamMember
        :> ZLocalUser
        :> "teams"
        :> Capture "tid" TeamId
        :> "members"
        :> QueryParam'
             [ Optional,
               Strict,
               Description "Maximum results to be returned"
             ]
             "maxResults"
             (Range 1 HardTruncationLimit Int32)
        :> QueryParam'
             [ Optional,
               Strict,
               Description
                 "Optional, when not specified, the first page will be returned.\
                 \Every returned page contains a `pagingState`, this should be supplied to retrieve the next page."
             ]
             "pagingState"
             TeamMembersPagingState
        :> Get '[JSON] TeamMembersPage
    )
    :<|> Named
           "get-team-member"
           ( Summary "Get single team member"
               :> CanThrow 'NotATeamMember
               :> CanThrow 'TeamMemberNotFound
               :> ZLocalUser
               :> "teams"
               :> Capture "tid" TeamId
               :> "members"
               :> Capture "uid" UserId
               :> Get '[JSON] TeamMemberOptPerms
           )
    :<|> Named
           "get-team-members-by-ids"
           ( Summary "Get team members by user id list"
               :> Description "The `has_more` field in the response body is always `false`."
               :> CanThrow 'NotATeamMember
               :> CanThrow 'BulkGetMemberLimitExceeded
               :> ZLocalUser
               :> "teams"
               :> Capture "tid" TeamId
               :> "get-members-by-ids-using-post"
               :> QueryParam'
                    [ Optional,
                      Strict,
                      Description "Maximum results to be returned"
                    ]
                    "maxResults"
                    (Range 1 HardTruncationLimit Int32)
               :> ReqBody '[JSON] User.UserIdList
               :> Post '[JSON] TeamMemberListOptPerms
           )
    :<|> Named
           "add-team-member"
           ( Summary "Add a new team member"
               :> Until 'V4
               :> CanThrow 'InvalidPermissions
               :> CanThrow 'NoAddToBinding
               :> CanThrow 'NotATeamMember
               :> CanThrow 'NotConnected
               :> CanThrow OperationDenied
               :> CanThrow 'TeamNotFound
               :> CanThrow 'TooManyTeamMembers
               :> CanThrow 'TooManyTeamAdmins
               :> CanThrow 'UserBindingExists
               :> CanThrow 'TooManyTeamMembersOnTeamWithLegalhold
               :> ZLocalUser
               :> ZConn
               :> "teams"
               :> Capture "tid" TeamId
               :> "members"
               :> ReqBody '[JSON] NewTeamMember
               :> MultiVerb1
                    'POST
                    '[JSON]
                    (RespondEmpty 200 "")
           )
    :<|> Named
           "delete-team-member"
           ( Summary "Remove an existing team member"
               :> CanThrow AuthenticationError
               :> CanThrow 'AccessDenied
               :> CanThrow 'TeamMemberNotFound
               :> CanThrow 'TeamNotFound
               :> CanThrow 'NotATeamMember
               :> CanThrow OperationDenied
               :> ZLocalUser
               :> ZConn
               :> "teams"
               :> Capture "tid" TeamId
               :> "members"
               :> Capture "uid" UserId
               :> ReqBody '[JSON] TeamMemberDeleteData
               :> MultiVerb
                    'DELETE
                    '[JSON]
                    TeamMemberDeleteResultResponseType
                    TeamMemberDeleteResult
           )
    :<|> Named
           "delete-non-binding-team-member"
           ( Summary "Remove an existing team member"
               :> Until 'V4
               :> CanThrow AuthenticationError
               :> CanThrow 'AccessDenied
               :> CanThrow 'TeamMemberNotFound
               :> CanThrow 'TeamNotFound
               :> CanThrow 'NotATeamMember
               :> CanThrow OperationDenied
               :> ZLocalUser
               :> ZConn
               :> "teams"
               :> Capture "tid" TeamId
               :> "members"
               :> Capture "uid" UserId
               :> MultiVerb
                    'DELETE
                    '[JSON]
                    TeamMemberDeleteResultResponseType
                    TeamMemberDeleteResult
           )
    :<|> Named
           "update-team-member"
           ( Summary "Update an existing team member"
               :> CanThrow 'AccessDenied
               :> CanThrow 'InvalidPermissions
               :> CanThrow 'TeamNotFound
               :> CanThrow 'TeamMemberNotFound
               :> CanThrow 'TooManyTeamAdmins
               :> CanThrow 'NotATeamMember
               :> CanThrow OperationDenied
               :> ZLocalUser
               :> ZConn
               :> "teams"
               :> Capture "tid" TeamId
               :> "members"
               :> ReqBody '[JSON] NewTeamMember
               :> MultiVerb1
                    'PUT
                    '[JSON]
                    (RespondEmpty 200 "")
           )
    :<|> Named
           "get-team-members-csv"
           ( Summary "Get all members of the team as a CSV file"
               :> CanThrow 'AccessDenied
               :> Description
                    "The endpoint returns data in chunked transfer encoding.\
                    \ Internal server errors might result in a failed transfer\
                    \ instead of a 500 response."
               :> ZLocalUser
               :> "teams"
               :> Capture "tid" TeamId
               :> "members"
               :> "csv"
               :> LowLevelStream
                    'GET
                    200
                    '[ '( "Content-Disposition",
                          "attachment; filename=\"wire_team_members.csv\""
                        )
                     ]
                    "CSV of team members"
                    CSV
           )
    :<|> Named
           "add-team-collaborator"
           ( Summary "Add a collaborator to the team."
               :> From 'V10
               :> ZLocalUser
               :> "teams"
               :> Capture "tid" TeamId
               :> "collaborators"
               :> ReqBody '[JSON] NewTeamCollaborator
               :> MultiVerb1 'POST '[JSON] (RespondEmpty 200 "")
           )
    :<|> Named
           "get-team-collaborators"
           ( Summary "Get all collaborators of the team."
               :> From 'V10
               :> ZLocalUser
               :> "teams"
               :> Capture "tid" TeamId
               :> "collaborators"
               :> MultiVerb1 'GET '[JSON] (Respond 200 "Return collaborators" [TeamCollaborator])
           )
    :<|> Named
           "update-team-collaborator"
           ( Summary "Update a collaborator permissions from the team."
               :> From 'V13
               :> ZLocalUser
               :> "teams"
               :> Capture "tid" TeamId
               :> "collaborators"
               :> Capture "uid" UserId
               :> ReqBody '[JSON] (Set CollaboratorPermission)
               :> MultiVerb1 'PUT '[JSON] (RespondEmpty 200 "")
           )
    :<|> Named
           "remove-team-collaborator"
           ( Summary "Remove a collaborator from the team."
               :> CanThrow OperationDenied
               :> CanThrow 'NotATeamMember
               :> From 'V12
               :> ZLocalUser
               :> "teams"
               :> Capture "tid" TeamId
               :> "collaborators"
               :> Capture "uid" UserId
               :> MultiVerb1 'DELETE '[JSON] (RespondEmpty 200 "")
           )

type TeamMemberDeleteResultResponseType =
  '[ RespondEmpty 202 "Team member scheduled for deletion",
     RespondEmpty 200 ""
   ]

data TeamMemberDeleteResult
  = TeamMemberDeleteAccepted
  | TeamMemberDeleteCompleted
  deriving (Generic)
  deriving (AsUnion TeamMemberDeleteResultResponseType) via GenericAsUnion TeamMemberDeleteResultResponseType TeamMemberDeleteResult

instance GSOP.Generic TeamMemberDeleteResult
