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

import Data.ZAuth.Validation qualified as ZAuth
import Imports
import Network.HTTP.Types.Status
import Network.Wai.Utilities.Error qualified as Wai
import Wire.API.Error
import Wire.API.Error.Brig qualified as E
import Wire.Error

-- | Authentication errors.
data AuthError
  = AuthInvalidUser
  | AuthInvalidCredentials
  | AuthSuspended
  | AuthEphemeral
  | AuthPendingInvitation
  deriving (Show, Eq)

instance Exception AuthError

-- | Re-authentication errors.
data ReAuthError
  = ReAuthError !AuthError
  | ReAuthMissingPassword
  | ReAuthCodeVerificationRequired
  | ReAuthCodeVerificationNoPendingCode
  | ReAuthCodeVerificationNoEmail
  deriving (Show, Eq)

instance Exception ReAuthError

data AuthenticationSubsystemError
  = AuthenticationSubsystemInvalidPasswordResetKey
  | AuthenticationSubsystemResetPasswordMustDiffer
  | AuthenticationSubsystemInvalidPasswordResetCode
  | AuthenticationSubsystemInvalidPhone
  | AuthenticationSubsystemAllowListError
  | AuthenticationSubsystemBadCredentials
  | AuthenticationSubsystemZAuthFailure ZAuth.Failure
  deriving (Eq, Show)

instance Exception AuthenticationSubsystemError

data VerificationCodeError
  = VerificationCodeRequired
  | VerificationCodeNoPendingCode
  | VerificationCodeNoEmail
  deriving (Show, Eq)

instance Exception VerificationCodeError

authenticationSubsystemErrorToHttpError :: AuthenticationSubsystemError -> HttpError
authenticationSubsystemErrorToHttpError =
  StdError . \case
    AuthenticationSubsystemInvalidPasswordResetKey -> errorToWai @E.InvalidPasswordResetKey
    AuthenticationSubsystemInvalidPasswordResetCode -> errorToWai @E.InvalidPasswordResetCode
    AuthenticationSubsystemResetPasswordMustDiffer -> errorToWai @E.ResetPasswordMustDiffer
    AuthenticationSubsystemInvalidPhone -> errorToWai @E.InvalidPhone
    AuthenticationSubsystemAllowListError -> errorToWai @E.AllowlistError
    AuthenticationSubsystemBadCredentials -> errorToWai @E.BadCredentials
    AuthenticationSubsystemZAuthFailure f -> zauthError f

zauthError :: ZAuth.Failure -> Wai.Error
zauthError ZAuth.Expired = authTokenExpired
zauthError ZAuth.Falsified = authTokenFalsified
zauthError ZAuth.Invalid = authTokenInvalid
zauthError ZAuth.Unsupported = authTokenUnsupported

reauthError :: ReAuthError -> HttpError
reauthError ReAuthMissingPassword = StdError (errorToWai @'E.MissingAuth)
reauthError (ReAuthError e) = authError e
reauthError ReAuthCodeVerificationRequired = StdError verificationCodeRequired
reauthError ReAuthCodeVerificationNoPendingCode = StdError verificationCodeNoPendingCode
reauthError ReAuthCodeVerificationNoEmail = StdError verificationCodeNoEmail

authError :: AuthError -> HttpError
authError AuthInvalidUser = StdError (errorToWai @'E.BadCredentials)
authError AuthInvalidCredentials = StdError (errorToWai @'E.BadCredentials)
authError AuthSuspended = StdError (errorToWai @'E.AccountSuspended)
authError AuthEphemeral = StdError (errorToWai @'E.AccountEphemeral)
authError AuthPendingInvitation = StdError (errorToWai @'E.AccountPending)

authTokenExpired :: Wai.Error
authTokenExpired = Wai.mkError status403 "invalid-credentials" "Zauth token expired"

authTokenInvalid :: Wai.Error
authTokenInvalid = Wai.mkError status403 "invalid-credentials" "Invalid zauth token"

authTokenFalsified :: Wai.Error
authTokenFalsified = Wai.mkError status403 "invalid-credentials" "Zauth token falsified"

authTokenUnsupported :: Wai.Error
authTokenUnsupported = Wai.mkError status403 "invalid-credentials" "Unsupported token operation for this token type"

verificationCodeAuthFailed :: Wai.Error
verificationCodeAuthFailed = Wai.mkError status403 "code-authentication-failed" "Code authentication failed."

verificationCodeRequired :: Wai.Error
verificationCodeRequired = Wai.mkError status403 "code-authentication-required" "Verification code required."

verificationCodeNoPendingCode :: Wai.Error
verificationCodeNoPendingCode = Wai.mkError status403 "code-authentication-failed" "Code authentication failed (no such code)."

verificationCodeNoEmail :: Wai.Error
verificationCodeNoEmail = Wai.mkError status403 "code-authentication-failed" "Code authentication failed (no such email)."
