{-# LANGUAGE RecordWildCards #-}

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

-- | Identify users for law enforcement.  (Wire has legal requirements to cooperate with the
-- authorities.  The wire backend operations team uses this to answer identification requests
-- manually.)
module Wire.API.Routes.Internal.Brig.EJPD
  ( EJPDRequestBody (EJPDRequestBody, ejpdRequestBody),
    EJPDResponseBody (EJPDResponseBody, ejpdResponseBody),
    EJPDResponseItemRoot (..),
    EJPDResponseItemLeaf (..),
    EJPDConvInfo (..),
    EJPDContact (..),
    EJPDTeamContacts (..),
    toEJPDResponseItemLeaf,
  )
where

import Data.Aeson qualified as Aeson
import Data.Handle (Handle)
import Data.Id (ConvId, TeamId, UserId)
import Data.OpenApi qualified as OpenAPI
import Data.Qualified
import Data.Schema
import Data.Set as Set
import Imports hiding (head)
import Test.QuickCheck (Arbitrary)
import Wire.API.Connection (Relation)
import Wire.API.Team.Member (NewListType)
import Wire.API.User.Identity (EmailAddress, Phone)
import Wire.API.User.Profile (Name)
import Wire.Arbitrary (GenericUniform (..))

newtype EJPDRequestBody = EJPDRequestBody {ejpdRequestBody :: [Handle]}
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform EJPDRequestBody)
  deriving (Aeson.ToJSON, Aeson.FromJSON, OpenAPI.ToSchema) via (Schema EJPDRequestBody)

newtype EJPDResponseBody = EJPDResponseBody {ejpdResponseBody :: [EJPDResponseItemRoot]}
  deriving stock (Eq, Show, Generic)
  deriving (Arbitrary) via (GenericUniform EJPDResponseBody)
  deriving (Aeson.ToJSON, Aeson.FromJSON, OpenAPI.ToSchema) via (Schema EJPDResponseBody)

data EJPDResponseItemRoot = EJPDResponseItemRoot
  { ejpdResponseRootUserId :: Qualified UserId,
    ejpdResponseRootTeamId :: Maybe TeamId,
    ejpdResponseRootName :: Name,
    ejpdResponseRootHandle :: Maybe Handle,
    ejpdResponseRootEmail :: Maybe EmailAddress,
    ejpdResponseRootPhone :: Maybe Phone,
    ejpdResponseRootPushTokens :: Set Text, -- 'Wire.API.Push.V2.Token.Token', but that would produce an orphan instance.
    ejpdResponseRootContacts :: Maybe (Set EJPDContact),
    ejpdResponseRootTeamContacts :: Maybe EJPDTeamContacts,
    ejpdResponseRootConversations :: Maybe (Set EJPDConvInfo),
    ejpdResponseRootAssets :: Maybe (Set Text) -- urls pointing to s3 resources
  }
  deriving stock (Eq, Ord, Show, Generic)
  deriving (Arbitrary) via (GenericUniform EJPDResponseItemRoot)

data EJPDResponseItemLeaf = EJPDResponseItemLeaf
  { ejpdResponseLeafUserId :: Qualified UserId,
    ejpdResponseLeafTeamId :: Maybe TeamId,
    ejpdResponseLeafName :: Name,
    ejpdResponseLeafHandle :: Maybe Handle,
    ejpdResponseLeafEmail :: Maybe EmailAddress,
    ejpdResponseLeafPhone :: Maybe Phone,
    ejpdResponseLeafPushTokens :: Set Text, -- 'Wire.API.Push.V2.Token.Token', but that would produce an orphan instance.
    ejpdResponseLeafConversations :: Maybe (Set EJPDConvInfo),
    ejpdResponseLeafAssets :: Maybe (Set Text) -- urls pointing to s3 resources
  }
  deriving stock (Eq, Ord, Show, Generic)
  deriving (Arbitrary) via (GenericUniform EJPDResponseItemLeaf)

data EJPDContact
  = -- | local or remote contact with relation
    EJPDContactFound
    { ejpdContactRelation :: Relation,
      ejpdContactFound :: EJPDResponseItemLeaf
    }
  deriving stock (Eq, Ord, Show, Generic)
  deriving (Arbitrary) via (GenericUniform EJPDContact)
  deriving (Aeson.ToJSON, Aeson.FromJSON, OpenAPI.ToSchema) via Schema EJPDContact

data EJPDTeamContacts = EJPDTeamContacts
  { ejpdTeamContacts :: Set EJPDResponseItemLeaf,
    ejpdTeamContactsListType :: NewListType
  }
  deriving stock (Eq, Ord, Show, Generic)
  deriving (Arbitrary) via (GenericUniform EJPDTeamContacts)

data EJPDConvInfo = EJPDConvInfo {ejpdConvName :: Text, ejpdConvId :: Qualified ConvId}
  deriving stock (Eq, Ord, Show, Generic)
  deriving (Arbitrary) via (GenericUniform EJPDConvInfo)
  deriving (Aeson.ToJSON, Aeson.FromJSON, OpenAPI.ToSchema) via Schema EJPDConvInfo

----------------------------------------------------------------------

toEJPDResponseItemLeaf :: EJPDResponseItemRoot -> EJPDResponseItemLeaf
toEJPDResponseItemLeaf EJPDResponseItemRoot {..} =
  EJPDResponseItemLeaf
    { ejpdResponseLeafUserId = ejpdResponseRootUserId,
      ejpdResponseLeafTeamId = ejpdResponseRootTeamId,
      ejpdResponseLeafName = ejpdResponseRootName,
      ejpdResponseLeafHandle = ejpdResponseRootHandle,
      ejpdResponseLeafEmail = ejpdResponseRootEmail,
      ejpdResponseLeafPhone = ejpdResponseRootPhone,
      ejpdResponseLeafPushTokens = ejpdResponseRootPushTokens,
      ejpdResponseLeafConversations = ejpdResponseRootConversations,
      ejpdResponseLeafAssets = ejpdResponseRootAssets
    }

----------------------------------------------------------------------

instance ToSchema EJPDRequestBody where
  schema = object do
    EJPDRequestBody <$> ejpdRequestBody .= field "EJPDRequest" (array schema)

instance ToSchema EJPDResponseBody where
  schema = object do
    EJPDResponseBody <$> ejpdResponseBody .= field "EJPDResponse" (array schema)

instance ToSchema EJPDResponseItemRoot where
  schema = object do
    EJPDResponseItemRoot
      <$> ejpdResponseRootUserId .= field "UserId" schema
      <*> ejpdResponseRootTeamId .= maybe_ (optField "TeamId" schema)
      <*> ejpdResponseRootName .= field "Name" schema
      <*> ejpdResponseRootHandle .= maybe_ (optField "Handle" schema)
      <*> ejpdResponseRootEmail .= maybe_ (optField "Email" schema)
      <*> ejpdResponseRootPhone .= maybe_ (optField "Phone" schema)
      <*> (Set.toList . ejpdResponseRootPushTokens) .= (Set.fromList <$> field "PushTokens" (array schema))
      <*> (fmap Set.toList . ejpdResponseRootContacts) .= (Set.fromList <$$> maybe_ (optField "Contacts" (array schema)))
      <*> ejpdResponseRootTeamContacts .= maybe_ (optField "TeamContacts" schema)
      <*> (fmap Set.toList . ejpdResponseRootConversations) .= (Set.fromList <$$> maybe_ (optField "Conversations" (array schema)))
      <*> (fmap Set.toList . ejpdResponseRootAssets) .= (Set.fromList <$$> maybe_ (optField "Assets" (array schema)))

instance ToSchema EJPDResponseItemLeaf where
  schema = object do
    EJPDResponseItemLeaf
      <$> ejpdResponseLeafUserId .= field "UserId" schema
      <*> ejpdResponseLeafTeamId .= maybe_ (optField "TeamId" schema)
      <*> ejpdResponseLeafName .= field "Name" schema
      <*> ejpdResponseLeafHandle .= maybe_ (optField "Handle" schema)
      <*> ejpdResponseLeafEmail .= maybe_ (optField "Email" schema)
      <*> ejpdResponseLeafPhone .= maybe_ (optField "Phone" schema)
      <*> (Set.toList . ejpdResponseLeafPushTokens) .= (Set.fromList <$> field "PushTokens" (array schema))
      <*> (fmap Set.toList . ejpdResponseLeafConversations) .= (Set.fromList <$$> maybe_ (optField "Conversations" (array schema)))
      <*> (fmap Set.toList . ejpdResponseLeafAssets) .= (Set.fromList <$$> maybe_ (optField "Assets" (array schema)))

instance ToSchema EJPDContact where
  schema =
    object do
      EJPDContactFound
        <$> ejpdContactRelation .= field "contact_relation" schema
        <*> ejpdContactFound .= field "contact_item" schema

instance ToSchema EJPDTeamContacts where
  schema = object do
    EJPDTeamContacts
      <$> (Set.toList . ejpdTeamContacts) .= (Set.fromList <$> field "TeamContacts" (array schema))
      <*> ejpdTeamContactsListType .= field "ListType" schema

instance ToSchema EJPDConvInfo where
  schema =
    object $
      EJPDConvInfo
        <$> ejpdConvName .= field "conv_name" schema
        <*> ejpdConvId .= field "conv_id" schema
