-- 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/>.

-- | Most of the errors thrown by galley are defined as static errors in
-- 'Wire.API.Error.Galley' and declared as part of the API. Errors defined here
-- are dynamic, and mostly internal.
module Galley.Types.Error
  ( -- * Internal errors
    InvalidInput (..),
    InternalError (..),
    internalErrorWithDescription,
    internalErrorDescription,
    legalHoldServiceUnavailable,
  )
where

import Data.Id
import Data.Text.Lazy as LT (pack)
import Imports
import Network.HTTP.Types.Status
import Network.Wai.Utilities (Error (message))
import Network.Wai.Utilities.Error qualified as Wai
import Wire.API.Error

data InternalError
  = BadConvState ConvId
  | BadMemberState
  | NoPrekeyForUser
  | CannotCreateManagedConv
  | InternalErrorWithDescription LText
  deriving (Eq)

internalErrorDescription :: InternalError -> LText
internalErrorDescription = message . internalErrorToWai

internalErrorToWai :: InternalError -> Wai.Error
internalErrorToWai (BadConvState convId) = badConvState convId
internalErrorToWai BadMemberState = Wai.mkError status500 "bad-state" "Bad internal member state."
internalErrorToWai NoPrekeyForUser = internalError
internalErrorToWai CannotCreateManagedConv = internalError
internalErrorToWai (InternalErrorWithDescription t) = internalErrorWithDescription t

instance APIError InternalError where
  toResponse = toResponse . internalErrorToWai

data InvalidInput
  = CustomRolesNotSupported
  | InvalidRange LText
  | InvalidUUID4
  | InvalidPayload LText
  | FederationFunctionNotSupported LText

instance APIError InvalidInput where
  toResponse CustomRolesNotSupported = toResponse $ badRequest "Custom roles not supported"
  toResponse (InvalidRange t) = toResponse $ invalidRange t
  toResponse InvalidUUID4 = toResponse invalidUUID4
  toResponse (InvalidPayload t) = toResponse $ invalidPayload t
  toResponse (FederationFunctionNotSupported t) = toResponse $ federationFunctionNotSupported t

----------------------------------------------------------------------------
-- Other errors

internalError :: Wai.Error
internalError = internalErrorWithDescription "internal error"

internalErrorWithDescription :: LText -> Wai.Error
internalErrorWithDescription = Wai.mkError status500 "internal-error"

invalidPayload :: LText -> Wai.Error
invalidPayload = Wai.mkError status400 "invalid-payload"

badRequest :: LText -> Wai.Error
badRequest = Wai.mkError status400 "bad-request"

federationFunctionNotSupported :: LText -> Wai.Error
federationFunctionNotSupported = Wai.mkError status400 "federation-function-not-supported"

invalidUUID4 :: Wai.Error
invalidUUID4 = Wai.mkError status400 "client-error" "Invalid UUID v4 format"

invalidRange :: LText -> Wai.Error
invalidRange = Wai.mkError status400 "client-error"

badConvState :: ConvId -> Wai.Error
badConvState cid =
  Wai.mkError status500 "bad-state" $
    "Connect conversation with more than 2 members: "
      <> LT.pack (show cid)

legalHoldServiceUnavailable :: (Show a) => a -> Wai.Error
legalHoldServiceUnavailable e = Wai.mkError status412 "legalhold-unavailable" ("legal hold service unavailable with underlying error: " <> (LT.pack . show $ e))
