
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}

module Gargantext.API.Routes.Named.Private (
  -- * Routes types
    GargPrivateAPI(..)
  , MkProtectedAPI
  , GargPrivateAPI'(..)
  , GargAdminAPI(..)
  , NodeAPIEndpoint(..)
  , MembersAPI(..)
  , IsGenericNodeRoute(..)
  , NotesProxy(..)
  ) where


import Data.Kind
import Data.Text (Text)
import Data.Text qualified as T
import GHC.Generics
import GHC.TypeLits
import Gargantext.API.Admin.Auth.Types
import Gargantext.API.Auth.PolicyCheck
import Gargantext.API.Routes.Named.Contact
import Gargantext.API.Routes.Named.Context
import Gargantext.API.Routes.Named.Corpus
import Gargantext.API.Routes.Named.Count
import Gargantext.API.Routes.Named.Document
import Gargantext.API.Routes.Named.List qualified as List
import Gargantext.API.Routes.Named.Node
import Gargantext.API.Routes.Named.Share
import Gargantext.API.Routes.Named.Table
import Gargantext.API.Routes.Named.Tree
import Gargantext.API.Routes.Named.Viz
import Gargantext.Database.Admin.Types.Hyperdata.Any
import Gargantext.Database.Admin.Types.Hyperdata.Corpus
import Gargantext.Database.Admin.Types.Node
import Servant.API
import Servant.Auth qualified as SA


type MkProtectedAPI private = SA.Auth '[SA.JWT, SA.Cookie] AuthenticatedUser :> private


newtype GargPrivateAPI mode = GargPrivateAPI
  { mkPrivateAPI :: mode :- MkProtectedAPI (NamedRoutes GargPrivateAPI')
  } deriving Generic


data GargPrivateAPI' mode = GargPrivateAPI'
  { gargAdminAPI :: mode :- NamedRoutes GargAdminAPI
  , nodeEp       :: mode :- NamedRoutes (NodeAPIEndpoint HyperdataAny)
  , contextEp    :: mode :- "context"  :> Summary "Context endpoint"
                                       :> Capture "node_id" ContextId
                                       :> NamedRoutes (ContextAPI HyperdataAny)
  , corpusNodeAPI :: mode :- NamedRoutes (NodeAPIEndpoint HyperdataCorpus)
  , corpusNodeNodeAPI :: mode :- "corpus" :> Summary "Corpus endpoint"
                                          :> Capture "node1_id" NodeId
                                          :> "document"
                                          :> Capture "node2_id" NodeId
                                          :> NamedRoutes (NodeNodeAPI HyperdataAny)
  , corpusExportAPI :: mode :- "corpus" :> Capture "node_id" CorpusId
                                        :> NamedRoutes CorpusExportAPI
  , annuaireEp :: mode :- NamedRoutes (NodeAPIEndpoint HyperdataAnnuaire)
  , contactAPI :: mode :- "annuaire" :> Summary "Contact endpoint"
                                     :> Capture "annuaire_id" NodeId
                                     :> NamedRoutes ContactAPI
  , tableNgramsAPI :: mode :- "document" :> Summary "Document endpoint"
                                         :> Capture "doc_id" DocId
                                         :> "ngrams"
                                         :> NamedRoutes TableNgramsAPI
  , documentExportAPI :: mode :- "texts" :> Capture "node_id" DocId
                                         :> NamedRoutes DocumentExportAPI
  , phyloExportAPI    :: mode :- "phylo" :> Capture "node_id" DocId
                                         :> NamedRoutes PhyloExportAPI
  , countAPI :: mode :- "count" :> Summary "Count endpoint"
                                :> ReqBody '[JSON] Query
                                :> NamedRoutes CountAPI
  , graphAPI :: mode :- "graph" :> Summary "Graph endpoint"
                                :> Capture "graph_id" NodeId
                                :> NamedRoutes GraphAPI
  , treeAPI :: mode :- "tree" :> Summary "Tree endpoint"
                              :> Capture "tree_id" NodeId
                              :> PolicyChecked (NamedRoutes NodeTreeAPI)
  , treeFlatAPI :: mode :- "treeflat" :> Summary "Flat tree endpoint"
                                      :> Capture "tree_id" NodeId
                                      :> NamedRoutes TreeFlatAPI
  , membersAPI     :: mode :- "members" :> Summary "Team node members" :> NamedRoutes MembersAPI
  , addWithFormEp  :: mode :- NamedRoutes AddWithForm
  , addWithQueryEp :: mode :- NamedRoutes AddWithQuery
  , listGetAPI     :: mode :- NamedRoutes List.GETAPI
  , listJsonAPI    :: mode :- NamedRoutes List.JSONAPI
  , listTsvAPI     :: mode :- NamedRoutes List.TSVAPI
  , shareUrlEp     :: mode :- "shareurl" :> NamedRoutes ShareURL
  } deriving Generic


data NotesProxy mode = NotesProxy
  { noteProxyEp :: mode :- Capture "frameId" T.Text
                        :> Raw
  } deriving Generic


data GargAdminAPI mode = GargAdminAPI
  { rootsEp       :: mode :- "user"  :> Summary "First user endpoint" :> NamedRoutes Roots
  , adminNodesAPI :: mode :- "nodes" :> Summary "Nodes endpoint"
                                     :> ReqBody '[JSON] [NodeId]
                                     :> NamedRoutes NodesAPI
  } deriving Generic

class IsGenericNodeRoute a where
  type family TyToSubPath (a :: Type) :: Symbol
  type family TyToCapture (a :: Type) :: Symbol
  type family TyToSummary (a :: Type) :: Type

instance IsGenericNodeRoute HyperdataAny where
  type instance TyToSubPath HyperdataAny = "node"
  type instance TyToCapture HyperdataAny = "node_id"
  type instance TyToSummary HyperdataAny = Summary "Node endpoint"

instance IsGenericNodeRoute HyperdataCorpus where
  type instance TyToSubPath HyperdataCorpus = "corpus"
  type instance TyToCapture HyperdataCorpus = "corpus_id"
  type instance TyToSummary HyperdataCorpus = Summary "Corpus endpoint"

instance IsGenericNodeRoute HyperdataAnnuaire where
  type instance TyToSubPath HyperdataAnnuaire = "annuaire"
  type instance TyToCapture HyperdataAnnuaire = "annuaire_id"
  type instance TyToSummary HyperdataAnnuaire = Summary "Annuaire endpoint"

newtype NodeAPIEndpoint a mode = NodeAPIEndpoint
  { nodeEndpointAPI :: mode :- TyToSubPath a
                            :> TyToSummary a
                            :> Capture (TyToCapture a) NodeId
                            :> NamedRoutes (NodeAPI a)
  } deriving Generic

newtype MembersAPI mode = MembersAPI
  { getMembersEp :: mode :- Get '[JSON] [Text]
  } deriving Generic

