-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2025 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.UserSubsystem.Error where

import Data.Text.Lazy qualified as LT
import Imports
import Network.HTTP.Types (status400, status403, status404)
import Network.Wai.Utilities qualified as Wai
import Wire.API.Error
import Wire.API.Error.Brig qualified as E
import Wire.Arbitrary
import Wire.Error

-- | All errors that are thrown by the user subsystem are subsumed under this sum type.
data UserSubsystemError
  = -- | user is managed by scim or e2ei is enabled
    --   FUTUREWORK(mangoiv): the name should probably resemble that
    UserSubsystemDisplayNameManagedByScim
  | UserSubsystemHandleManagedByScim
  | UserSubsystemLocaleManagedByScim
  | UserSubsystemEmailManagedByScim
  | UserSubsystemNoIdentity
  | UserSubsystemLastIdentity
  | UserSubsystemHandleExists
  | UserSubsystemInvalidHandle
  | UserSubsystemProfileNotFound
  | UserSubsystemInsufficientPermissions
  | UserSubsystemCannotJoinMultipleTeams
  | UserSubsystemTooManyTeamMembers
  | UserSubsystemMissingIdentity
  | UserSubsystemInvalidActivationCodeWrongUser
  | UserSubsystemInvalidActivationCodeWrongCode
  | UserSubsystemInvalidInvitationCode
  | UserSubsystemInvitationNotFound
  | UserSubsystemUserNotAllowedToJoinTeam Wai.Error
  | UserSubsystemMLSServicesNotAllowed
  | UserSubsystemChangeBlocklistedEmail
  | UserSubsystemEmailExists
  | UserSubsystemGuardFailed GuardFailure
  | UserSubsystemMlsRemovalNotAllowed
  | UserSubsystemBlockedDomain
  deriving (Eq, Show)

data GuardFailure
  = DomRedirSetToSSO
  | DomRedirSetToBackend
  | DomRedirSetToNoRegistration
  | TeamInviteSetToNotAllowed
  | TeamInviteRestrictedToOtherTeam
  | InvalidDomain String
  deriving (Show, Eq, Generic)
  deriving (Arbitrary) via (GenericUniform GuardFailure)

userSubsystemErrorToHttpError :: UserSubsystemError -> HttpError
userSubsystemErrorToHttpError =
  StdError . \case
    UserSubsystemProfileNotFound -> errorToWai @E.UserNotFound
    UserSubsystemDisplayNameManagedByScim -> errorToWai @E.NameManagedByScim
    UserSubsystemLocaleManagedByScim -> errorToWai @E.LocaleManagedByScim
    UserSubsystemEmailManagedByScim -> errorToWai @E.EmailManagedByScim
    UserSubsystemNoIdentity -> errorToWai @E.NoIdentity
    UserSubsystemLastIdentity -> errorToWai @E.LastIdentity
    UserSubsystemHandleExists -> errorToWai @E.HandleExists
    UserSubsystemInvalidHandle -> errorToWai @E.InvalidHandle
    UserSubsystemHandleManagedByScim -> errorToWai @E.HandleManagedByScim
    UserSubsystemInsufficientPermissions -> errorToWai @E.InsufficientPermissions
    UserSubsystemCannotJoinMultipleTeams -> errorToWai @E.CannotJoinMultipleTeams
    UserSubsystemTooManyTeamMembers -> errorToWai @E.TooManyTeamMembers
    UserSubsystemMissingIdentity -> errorToWai @E.MissingIdentity
    UserSubsystemInvalidActivationCodeWrongUser -> errorToWai @E.InvalidActivationCodeWrongUser
    UserSubsystemInvalidActivationCodeWrongCode -> errorToWai @E.InvalidActivationCodeWrongCode
    UserSubsystemInvalidInvitationCode -> errorToWai @E.InvalidInvitationCode
    UserSubsystemInvitationNotFound -> Wai.mkError status404 "not-found" "Something went wrong, while looking up the invitation"
    UserSubsystemUserNotAllowedToJoinTeam e -> e
    UserSubsystemMLSServicesNotAllowed -> errorToWai @E.MLSServicesNotAllowed
    UserSubsystemChangeBlocklistedEmail -> errorToWai @E.BlacklistedEmail
    UserSubsystemEmailExists -> errorToWai @'E.UserKeyExists
    UserSubsystemGuardFailed err ->
      let e403 msg = Wai.mkError status403 "condition-failed" msg
          e400 msg = Wai.mkError status400 "invalid-domain" (LT.pack msg)
       in case err of
            DomRedirSetToSSO -> e403 "`domain_redirect` is set to `sso:{code}`"
            DomRedirSetToBackend -> e403 "`domain_redirect` is set to `backend`"
            DomRedirSetToNoRegistration -> e403 "`domain_redirect` is set to `no-registration`"
            TeamInviteSetToNotAllowed -> e403 "`teamInvite` is set to `not-allowed`"
            TeamInviteRestrictedToOtherTeam -> e403 "`teamInvite` is restricted to another team."
            InvalidDomain parseErr -> e400 parseErr
    UserSubsystemMlsRemovalNotAllowed -> errorToWai @E.MlsRemovalNotAllowed
    UserSubsystemBlockedDomain -> errorToWai @E.CustomerExtensionBlockedDomain

instance Exception UserSubsystemError
