{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StrictData #-}

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

-- | Types for the (internal) provider API.
--
-- FUTUREWORK: Deduplicate with 'Wire.API.User'?
module Wire.API.Provider
  ( -- * Provider
    Provider (..),
    ProviderProfile (..),

    -- * NewProvider
    NewProvider (..),
    NewProviderResponse (..),

    -- * UpdateProvider
    UpdateProvider (..),

    -- * ProviderActivationResponse
    ProviderActivationResponse (..),
    ProviderLogin (..),
    DeleteProvider (..),

    -- * Password Change/Reset
    PasswordReset (..),
    CompletePasswordReset (..),
    PasswordChange (..),
    EmailUpdate (..),

    -- * Re-exports
    HttpsUrl (..),
    ServiceToken (..),
    ServiceTag (..),
  )
where

import Data.Aeson qualified as A
import Data.Id
import Data.Misc (HttpsUrl (..), PlainTextPassword6, PlainTextPassword8)
import Data.OpenApi qualified as S
import Data.Range
import Data.Schema
import Imports
import Wire.API.Conversation.Code as Code
import Wire.API.Provider.Service (ServiceToken (..))
import Wire.API.Provider.Service.Tag (ServiceTag (..))
import Wire.API.User.EmailAddress
import Wire.API.User.Profile (Name)
import Wire.Arbitrary (Arbitrary, GenericUniform (..))

--------------------------------------------------------------------------------
-- Provider

-- | Full provider definition as seen by a verified provider itself.
data Provider = Provider
  { providerId :: ProviderId,
    providerName :: Name,
    providerEmail :: EmailAddress,
    providerUrl :: HttpsUrl,
    providerDescr :: Text
  }
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform Provider)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema Provider

instance ToSchema Provider where
  schema =
    object $
      Provider
        <$> providerId .= field "id" schema
        <*> providerName .= field "name" schema
        <*> providerEmail .= field "email" schema
        <*> providerUrl .= field "url" schema
        <*> providerDescr .= field "description" schema

-- | A provider profile as seen by regular users.
-- Note: This is a placeholder that may evolve to contain only a subset of
-- the full provider information.
newtype ProviderProfile = ProviderProfile Provider
  deriving stock (Eq, Show)
  deriving newtype (A.FromJSON, A.ToJSON, Arbitrary, S.ToSchema)

--------------------------------------------------------------------------------
-- NewProvider

-- | Input data for registering a new provider.
data NewProvider = NewProvider
  { newProviderName :: Name,
    newProviderEmail :: EmailAddress,
    newProviderUrl :: HttpsUrl,
    newProviderDescr :: Range 1 1024 Text,
    -- | If none provided, a password is generated.
    newProviderPassword :: Maybe PlainTextPassword6
  }
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform NewProvider)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema NewProvider

instance ToSchema NewProvider where
  schema =
    object $
      NewProvider
        <$> newProviderName .= field "name" schema
        <*> newProviderEmail .= field "email" schema
        <*> newProviderUrl .= field "url" schema
        <*> newProviderDescr .= field "description" schema
        <*> newProviderPassword .= maybe_ (optField "password" schema)

-- | Response data upon registering a new provider.
data NewProviderResponse = NewProviderResponse
  { rsNewProviderId :: ProviderId,
    -- | The generated password, if none was provided
    -- in the 'NewProvider' request.
    rsNewProviderPassword :: Maybe PlainTextPassword8
  }
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform NewProviderResponse)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema NewProviderResponse

instance ToSchema NewProviderResponse where
  schema =
    object $
      NewProviderResponse
        <$> rsNewProviderId .= field "id" schema
        <*> rsNewProviderPassword .= maybe_ (optField "password" schema)

--------------------------------------------------------------------------------
-- UpdateProvider

-- | Input data for updating general provider information.
data UpdateProvider = UpdateProvider
  { updateProviderName :: Maybe Name,
    updateProviderUrl :: Maybe HttpsUrl,
    updateProviderDescr :: Maybe Text
  }
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform UpdateProvider)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema UpdateProvider

instance ToSchema UpdateProvider where
  schema =
    object $
      UpdateProvider
        <$> updateProviderName .= maybe_ (optField "name" schema)
        <*> updateProviderUrl .= maybe_ (optField "url" schema)
        <*> updateProviderDescr .= maybe_ (optField "description" schema)

--------------------------------------------------------------------------------
-- ProviderActivationResponse

-- | Successful response upon activating an email address (or possibly phone
-- number in the future) of a provider.
newtype ProviderActivationResponse = ProviderActivationResponse
  {activatedProviderIdentity :: EmailAddress}
  deriving stock (Eq, Show)
  deriving newtype (Arbitrary)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema ProviderActivationResponse

instance ToSchema ProviderActivationResponse where
  schema =
    object $
      ProviderActivationResponse
        <$> activatedProviderIdentity .= field "email" schema

--------------------------------------------------------------------------------
-- ProviderLogin

-- | Input data for a provider login request.
data ProviderLogin = ProviderLogin
  { providerLoginEmail :: EmailAddress,
    providerLoginPassword :: PlainTextPassword6
  }
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform ProviderLogin)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema ProviderLogin

instance ToSchema ProviderLogin where
  schema =
    object $
      ProviderLogin
        <$> providerLoginEmail .= field "email" schema
        <*> providerLoginPassword .= field "password" schema

--------------------------------------------------------------------------------
-- DeleteProvider

-- | Input data for a provider deletion request.
-- | FUTUREWORK: look into a phase out of PlainTextPassword6
newtype DeleteProvider = DeleteProvider
  {deleteProviderPassword :: PlainTextPassword6}
  deriving stock (Eq, Show)
  deriving newtype (Arbitrary)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema DeleteProvider

instance ToSchema DeleteProvider where
  schema =
    object $
      DeleteProvider
        <$> deleteProviderPassword .= field "password" schema

--------------------------------------------------------------------------------
-- Password Change/Reset

-- | The payload for initiating a password reset.
newtype PasswordReset = PasswordReset {email :: EmailAddress}
  deriving stock (Eq, Show)
  deriving newtype (Arbitrary)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema PasswordReset

instance ToSchema PasswordReset where
  schema =
    object $
      PasswordReset
        <$> (.email) .= field "email" schema

-- | The payload for completing a password reset.
data CompletePasswordReset = CompletePasswordReset
  { key :: Code.Key,
    code :: Code.Value,
    password :: PlainTextPassword6
  }
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform CompletePasswordReset)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema CompletePasswordReset

instance ToSchema CompletePasswordReset where
  schema =
    object $
      CompletePasswordReset
        <$> key .= field "key" schema
        <*> (.code) .= field "code" schema
        <*> (.password) .= field "password" schema

-- | The payload for changing a password.
data PasswordChange = PasswordChange
  { oldPassword :: PlainTextPassword6,
    newPassword :: PlainTextPassword6
  }
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform PasswordChange)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema PasswordChange

instance ToSchema PasswordChange where
  schema =
    object $
      PasswordChange
        <$> oldPassword .= field "old_password" schema
        <*> newPassword .= field "new_password" schema

-- | The payload for updating an email address
newtype EmailUpdate = EmailUpdate {email :: EmailAddress}
  deriving stock (Eq, Show, Generic)
  deriving newtype (Arbitrary)
  deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema EmailUpdate

instance ToSchema EmailUpdate where
  schema =
    object $
      EmailUpdate
        <$> (.email) .= field "email" schema
