{-# LANGUAGE InstanceSigs         #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE ViewPatterns         #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

{--| Collection of ready-to-use servant-client functions to use in all our specs. -}

module Test.API.Routes (

  -- * Constants and helpers
    curApi
  , mkUrl
  , gqlUrl
  , toServantToken
  , clientRoutes

  -- * Servant client functions
  , auth_api
  , get_children
  , get_node
  , get_table
  , get_table_ngrams
  , get_tree
  , move_node
  , publish_node
  , put_table_ngrams
  , update_node
  , delete_node
  , add_form_to_list
  , add_tsv_to_list
  , get_corpus_sqlite_export
  , addTeamMember
  , importCorpus
  ) where

import Data.Text.Encoding qualified as TE
import Fmt (Builder, (+|), (|+))
import Gargantext.API.Admin.Auth.Types (AuthRequest, AuthResponse, Token)
import Gargantext.API.Errors
import Gargantext.API.HashedResponse (HashedResponse)
import Gargantext.API.Ngrams.List.Types (WithJsonFile, WithTextFile)
import Gargantext.API.Ngrams.Types ( NgramsTable, NgramsTablePatch, OrderBy, TabType, Versioned, VersionedWithCount )
import Gargantext.API.Node.Corpus.Export.Types (CorpusSQLite)
import Gargantext.API.Node.Share.Types (ShareNodeParams(..))
import Gargantext.API.Routes.Client
import Gargantext.API.Routes.Named
import Gargantext.API.Routes.Named.Corpus (CorpusExportAPI(corpusSQLiteEp))
import Gargantext.API.Routes.Named.List (updateListJSONEp, updateListTSVEp)
import Gargantext.API.Routes.Named.Node hiding (treeAPI)
import Gargantext.API.Routes.Named.Private hiding (tableNgramsAPI)
import Gargantext.API.Routes.Named.Publish (PublishAPI(..), PublishRequest(..))
import Gargantext.API.Routes.Named.Share (shareNodeEp)
import Gargantext.API.Routes.Named.Table
import Gargantext.API.Routes.Named.Tree (nodeTreeEp)
import Gargantext.API.Types () -- MimeUnrender instances
import Gargantext.API.Worker (workerAPIPost)
import Gargantext.Core.Text.Corpus.Query (RawQuery)
import Gargantext.Core.Types
import Gargantext.Core.Types.Query (Limit, MaxSize, MinSize, Offset)
import Gargantext.Core.Worker.Types (JobInfo)
import Gargantext.Database.Admin.Types.Hyperdata
import Gargantext.Database.Query.Facet qualified as Facet
import Gargantext.Database.Query.Table.NodeNode (SourceId(..), TargetId(..))
import Gargantext.Prelude
import Network.Wai.Handler.Warp (Port)
import Servant (Headers, Header)
import Servant.Auth.Client qualified as S
import Servant.Client.Streaming
import Servant.Conduit ()
import Gargantext.API.Routes.Named.Corpus (addWithTempFileEp)
import Gargantext.API.Node.Types (NewWithForm)


-- This is for requests made by http.client directly to hand-crafted URLs.
curApi :: Builder
curApi = "v1.0"

mkUrl :: Port -> Builder -> ByteString
mkUrl _port urlPiece =
  "/api/" +| curApi |+ urlPiece

gqlUrl :: ByteString
gqlUrl = "/gql"

-- This is for Servant.Client requests
auth_api :: AuthRequest -> ClientM AuthResponse
auth_api = clientRoutes & apiWithCustomErrorScheme
                        & ($ GES_new)
                        & backendAPI
                        & backendAPI'
                        & mkBackEndAPI
                        & gargAPIVersion
                        & gargAuthAPI
                        & authEp

toServantToken :: Token -> S.Token
toServantToken = S.Token . TE.encodeUtf8

get_node :: Token
         -> NodeId
         -> ClientM (Node HyperdataAny)
get_node (toServantToken -> token) nodeId =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ nodeId)
               & nodeNodeAPI
               & getNodeEp

update_node :: Token
            -> NodeId
            -> UpdateNodeParams
            -> ClientM JobInfo
update_node (toServantToken -> token) nodeId params =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ nodeId)
               & updateAPI
               & updateNodeEp
               & workerAPIPost
               & (\submitForm -> submitForm params)

add_form_to_list :: Token
                 -> ListId
                 -> WithJsonFile
                 -> ClientM JobInfo
add_form_to_list (toServantToken -> token) listId params =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & listJsonAPI
               & updateListJSONEp
               & ($ listId)
               & workerAPIPost
               & (\submitForm -> submitForm params)

add_tsv_to_list :: Token
                -> ListId
                -> WithTextFile
                -> ClientM JobInfo
add_tsv_to_list (toServantToken -> token) listId params =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & listTsvAPI
               & updateListTSVEp
               & ($ listId)
               & workerAPIPost
               & (\submitForm -> submitForm params)


get_table_ngrams :: Token
                 -> NodeId
                 -> TabType
                 -> ListId
                 -> Limit
                 -> Maybe Offset
                 -> Maybe ListType
                 -> Maybe MinSize
                 -> Maybe MaxSize
                 -> Maybe OrderBy
                 -> Maybe Text
                 -> ClientM (VersionedWithCount NgramsTable)
get_table_ngrams (toServantToken -> token) nodeId =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ nodeId)
               & tableNgramsAPI
               & tableNgramsGetAPI
               & getNgramsTableEp

put_table_ngrams :: Token
                 -> NodeId
                 -> TabType
                 -> ListId
                 -> Versioned NgramsTablePatch
                 -> ClientM (Versioned NgramsTablePatch)
put_table_ngrams (toServantToken -> token) nodeId =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ nodeId)
               & tableNgramsAPI
               & tableNgramsPutAPI
               & putNgramsTableEp

get_table :: Token
          -> NodeId
          -> Maybe TabType
          -> Maybe Limit
          -> Maybe Offset
          -> Maybe Facet.OrderBy
          -> Maybe RawQuery
          -> Maybe Text
          -> ClientM (HashedResponse FacetTableResult)
get_table (toServantToken -> token) nodeId =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ nodeId)
               & tableAPI
               & getTableEp

get_children :: Token
             -> NodeId
             -> Maybe NodeType
             -> Maybe Offset
             -> Maybe Limit
             -> ClientM (NodeTableResult HyperdataAny)
get_children (toServantToken -> token) nodeId =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ nodeId)
               & childrenAPI
               & summaryChildrenEp

get_tree :: Token -> NodeId -> ClientM (Tree NodeTree)
get_tree (toServantToken -> token) nId = do
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & treeAPI
               & ($ nId)
               & nodeTreeEp
               & ($ [])

move_node :: Token -> SourceId -> TargetId -> ClientM [NodeId]
move_node (toServantToken -> token) (SourceId sourceId) (TargetId targetId) = fmap (map UnsafeMkNodeId) $
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ sourceId)
               & moveAPI
               & moveNodeEp
               & ($ targetId)

delete_node :: Token
            -> NodeId
            -> ClientM Int
delete_node (toServantToken -> token) nodeId =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ nodeId)
               & deleteEp

publish_node :: Token -> NodeId -> NodePublishPolicy -> ClientM NodeId
publish_node (toServantToken -> token) sourceId policy = fmap UnsafeMkNodeId $
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ sourceId)
               & publishAPI
               & publishEp
               & ($ PublishRequest policy)


get_corpus_sqlite_export :: Token
                         -> CorpusId
                         -> ClientM (Headers '[Header "Content-Disposition" Text] CorpusSQLite)
get_corpus_sqlite_export (toServantToken -> token) cId =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & corpusExportAPI
               & ($ cId)
               & corpusSQLiteEp
               & ($ Nothing)  -- Maybe ListId


addTeamMember :: Token -> NodeId -> ShareNodeParams -> ClientM NodeId
addTeamMember (toServantToken -> token) nodeId params = fmap UnsafeMkNodeId $
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & nodeEp
               & nodeEndpointAPI
               & ($ nodeId)
               & shareAPI
               & shareNodeEp
               & ($ params)

importCorpus :: Token -> CorpusId -> NewWithForm -> ClientM JobInfo
importCorpus (toServantToken -> token) corpusId params =
  clientRoutes & apiWithCustomErrorScheme
               & ($ GES_new)
               & backendAPI
               & backendAPI'
               & mkBackEndAPI
               & gargAPIVersion
               & gargPrivateAPI
               & mkPrivateAPI
               & ($ token)
               & addWithTempFile
               & addWithTempFileEp
               & ($ corpusId)
               & workerAPIPost
               & (\submitForm -> submitForm params)
