
module Gargantext.API.Server.Named.Ngrams (
  -- * Server handlers
    apiNgramsTableCorpus
  , apiNgramsTableDoc
  ) where

import Control.Lens ((%%~))
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Gargantext.API.Admin.Auth (withNamedAccess)
import Gargantext.API.Admin.Auth.Types (AuthenticatedUser, PathId (..))
import Gargantext.API.Admin.EnvTypes
import Gargantext.API.Admin.Orchestrator.Types (AsyncJobs(..))
import Gargantext.API.Errors.Types (BackendInternalError)
import Gargantext.API.Metrics qualified as Metrics
import Gargantext.API.Ngrams
import Gargantext.API.Ngrams.Types
import Gargantext.API.Prelude
import Gargantext.API.Routes.Named.Table qualified as Named
import Gargantext.Core.NodeStory.Types (HasNodeStory)
import Gargantext.Core.Types hiding (Terms)
import Gargantext.Core.Types.Query (Limit(..), Offset(..))
import Gargantext.Database.Admin.Config (userMaster)
import Gargantext.Database.Query.Table.Ngrams ( selectNgramsByDoc )
import Gargantext.Database.Query.Table.Node (getNode)
import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
import Gargantext.Database.Query.Table.Node.Select ( selectNodesWithUsername )
import Gargantext.Database.Schema.Node (node_id, node_parent_id, node_user_id)
import Gargantext.Prelude
import Gargantext.Utils.Jobs (serveJobsAPI)
import Gargantext.Utils.Jobs.Monad
import Servant.Server.Generic (AsServerT)


apiNgramsTableCorpus :: NodeId -> Named.TableNgramsAPI (AsServerT (GargM Env BackendInternalError))
apiNgramsTableCorpus cId = Named.TableNgramsAPI
  { tableNgramsGetAPI       = Named.TableNgramsApiGet $ getTableNgramsCorpus       cId
  , tableNgramsPutAPI       = Named.TableNgramsApiPut $ tableNgramsPut
  , recomputeScoresEp       = Named.RecomputeScoresNgramsApiGet $ scoresRecomputeTableNgrams cId
  , tableNgramsGetVersionEp = Named.TableNgramsApiGetVersion $ getTableNgramsVersion      cId
  , tableNgramsAsyncAPI     = apiNgramsAsync             cId
  }


apiNgramsTableDoc :: AuthenticatedUser
                  -> DocId
                  -> Named.TableNgramsAPI (AsServerT (GargM Env BackendInternalError))
apiNgramsTableDoc uid dId = withNamedAccess uid (PathNode dId) $ Named.TableNgramsAPI
  { tableNgramsGetAPI       = Named.TableNgramsApiGet $ getTableNgramsDoc dId
  , tableNgramsPutAPI       = Named.TableNgramsApiPut tableNgramsPut
  , recomputeScoresEp       = Named.RecomputeScoresNgramsApiGet $ scoresRecomputeTableNgrams dId
  , tableNgramsGetVersionEp = Named.TableNgramsApiGetVersion $ getTableNgramsVersion dId
  , tableNgramsAsyncAPI     = apiNgramsAsync dId
  }


getTableNgramsVersion :: ( HasNodeStory env err m
                         , HasNodeError err )
                      => NodeId
                      -> TabType
                      -> ListId
                      -> m Version
getTableNgramsVersion _nId _tabType listId = currentVersion listId


apiNgramsAsync :: NodeId -> Named.TableNgramsAsyncAPI (AsServerT (GargM Env BackendInternalError))
apiNgramsAsync _dId = Named.TableNgramsAsyncAPI $ AsyncJobs $
  serveJobsAPI TableNgramsJob $ \jHandle i -> withTracer (printDebug "tableNgramsPostChartsAsync") jHandle $
    \jHandle' -> tableNgramsPostChartsAsync i jHandle'


tableNgramsPostChartsAsync :: ( HasNodeStory env err m
                              , MonadJobStatus m )
                            => UpdateTableNgramsCharts
                            -> JobHandle m
                            -> m ()
tableNgramsPostChartsAsync utn jobHandle = do
      let tabType = utn ^. utn_tab_type
      let listId = utn ^. utn_list_id

      node <- getNode listId
      let _nId = node ^. node_id
          _uId = node ^. node_user_id
          mCId = node ^. node_parent_id

      -- printDebug "[tableNgramsPostChartsAsync] tabType" tabType
      -- printDebug "[tableNgramsPostChartsAsync] listId" listId

      case mCId of
        Nothing -> do
          -- printDebug "[tableNgramsPostChartsAsync] can't update charts, no parent, nId" nId
          markStarted 1 jobHandle
          markFailedNoErr jobHandle
        Just cId -> do
          case tabType of
            Authors -> do
              -- printDebug "[tableNgramsPostChartsAsync] Authors, updating Pie, cId" cId
              markStarted 1 jobHandle
              _ <- Metrics.updatePie cId (Just listId) tabType Nothing
              markComplete jobHandle
            Institutes -> do
              -- printDebug "[tableNgramsPostChartsAsync] Institutes, updating Tree, cId" cId
              -- printDebug "[tableNgramsPostChartsAsync] updating tree StopTerm, cId" cId
              markStarted 3 jobHandle
              _ <- Metrics.updateTree cId (Just listId) tabType StopTerm
              -- printDebug "[tableNgramsPostChartsAsync] updating tree CandidateTerm, cId" cId
              markProgress 1 jobHandle
              _ <- Metrics.updateTree cId (Just listId) tabType CandidateTerm
              -- printDebug "[tableNgramsPostChartsAsync] updating tree MapTerm, cId" cId
              markProgress 1 jobHandle
              _ <- Metrics.updateTree cId (Just listId) tabType MapTerm
              markComplete jobHandle
            Sources -> do
              -- printDebug "[tableNgramsPostChartsAsync] Sources, updating chart, cId" cId
              markStarted 1 jobHandle
              _ <- Metrics.updatePie cId (Just listId) tabType Nothing
              markComplete jobHandle
            Terms -> do
              -- printDebug "[tableNgramsPostChartsAsync] Terms, updating Metrics (Histo), cId" cId
              markStarted 6 jobHandle
{-
              _ <- Metrics.updateChart cId listId tabType Nothing
              logRefSuccess
              _ <- Metrics.updatePie cId (Just listId) tabType Nothing
              logRefSuccess
              _ <- Metrics.updateScatter cId (Just listId) tabType Nothing
              logRefSuccess
              _ <- Metrics.updateTree cId (Just listId) tabType StopTerm
              logRefSuccess
              _ <- Metrics.updateTree cId (Just listId) tabType CandidateTerm
              logRefSuccess
              _ <- Metrics.updateTree cId (Just listId) tabType MapTerm
-}
              markComplete jobHandle
            _otherTabType -> do
              -- printDebug "[tableNgramsPostChartsAsync] no update for tabType = " tabType
              markStarted 1 jobHandle
              markFailedNoErr jobHandle

  {-
  { _ne_list        :: ListType
  If we merge the parents/children we can potentially create cycles!
  , _ne_parent      :: Maybe NgramsTerm
  , _ne_children    :: MSet NgramsTerm
  }
  -}

scoresRecomputeTableNgrams :: forall env err m.
                              ( HasNodeStory env err m, HasNodeError err )
                           => NodeId -> TabType -> ListId -> m Int
scoresRecomputeTableNgrams nId tabType listId = do
  tableMap <- getNgramsTableMap listId ngramsType
  _ <- tableMap & v_data %%~ (setNgramsTableScores nId listId ngramsType)
                           . Map.mapWithKey ngramsElementFromRepo

  pure $ 1
  where
    ngramsType = ngramsTypeFromTabType tabType

-- | Text search is deactivated for now for ngrams by doc only
getTableNgramsDoc :: ( HasNodeStory env err m
                     , HasNodeError err )
                  => DocId -> TabType
                  -> ListId -> Limit -> Maybe Offset
                  -> Maybe ListType
                  -> Maybe MinSize -> Maybe MaxSize
                  -> Maybe OrderBy
                  -> Maybe Text -- full text search
                  -> m (VersionedWithCount NgramsTable)
getTableNgramsDoc dId tabType listId limit_ offset listType minSize maxSize orderBy _mt = do
  ns <- selectNodesWithUsername NodeList userMaster
  let ngramsType = ngramsTypeFromTabType tabType
  ngs <- selectNgramsByDoc (ns <> [listId]) dId ngramsType
  let searchQueryFn (NgramsTerm nt) = Set.member nt (Set.fromList ngs)
      searchQuery = NgramsSearchQuery {
                    _nsq_limit       = limit_
                  , _nsq_offset      = offset
                  , _nsq_listType    = listType
                  , _nsq_minSize     = minSize
                  , _nsq_maxSize     = maxSize
                  , _nsq_orderBy     = orderBy
                  , _nsq_searchQuery = searchQueryFn
                  }
  getTableNgrams dId listId tabType searchQuery


