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

import Control.Lens ((%%~), view)
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Gargantext.API.Admin.Auth.Types (AuthenticatedUser, PathId (..))
import Gargantext.API.Admin.Auth (withNamedAccess)
import Gargantext.API.Admin.EnvTypes (Env)
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 (GargM)
import Gargantext.API.Routes.Named.Table qualified as Named
import Gargantext.API.Worker (serveWorkerAPI)
import Gargantext.Core.NodeStory.Types (HasNodeStory, NodeStoryEnv, hasNodeStory, HasNodeStoryEnv)
import Gargantext.Core.Types (DocId, ListId, ListType(..), NodeId, NodeType(..))
import Gargantext.Core.Types.Query (Limit(..), Offset(..))
import Gargantext.Core.Worker.Jobs.Types qualified as Jobs
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.Select ( selectNodesWithUsername )
import Gargantext.Database.Schema.Node (node_id, node_parent_id, node_user_id)
import Gargantext.Database.Prelude
import Gargantext.Prelude
import Gargantext.Utils.Jobs.Monad (JobHandle, MonadJobStatus(..), markFailedNoErr)
import Servant.Server.Generic (AsServerT)


apiNgramsTableCorpus :: NodeId -> Named.TableNgramsAPI (AsServerT (GargM Env BackendInternalError))
apiNgramsTableCorpus cId = Named.TableNgramsAPI
  { tableNgramsGetAPI       = Named.TableNgramsApiGet $ getTableNgramsCorpusHandler cId
  , tableNgramsPutAPI       = Named.TableNgramsApiPut $ tableNgramsPut
  , recomputeScoresEp       = Named.RecomputeScoresNgramsApiGet $ scoresRecomputeTableNgramsHandler 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 $ getTableNgramsDocHandler dId
  , tableNgramsPutAPI       = Named.TableNgramsApiPut tableNgramsPut
  , recomputeScoresEp       = Named.RecomputeScoresNgramsApiGet $ scoresRecomputeTableNgramsHandler dId
  , tableNgramsGetVersionEp = Named.TableNgramsApiGetVersion $ getTableNgramsVersion dId
  , tableNgramsAsyncAPI     = apiNgramsAsync dId
  }


getTableNgramsVersion :: (IsDBCmd err env m, HasNodeStoryEnv err env)
                      => NodeId
                      -> TabType
                      -> ListId
                      -> m Version
getTableNgramsVersion _nId _tabType listId = runDBQuery $ currentVersion listId


apiNgramsAsync :: NodeId -> Named.TableNgramsAsyncAPI (AsServerT (GargM Env BackendInternalError))
apiNgramsAsync nId =
  Named.TableNgramsAsyncAPI {
    updateTableNgramsChartsEp = serveWorkerAPI $ \p ->
        Jobs.NgramsPostCharts { Jobs._npc_node_id = nId
                              , Jobs._npc_args    = p }
    }

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 <- runDBQuery $ 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
  }
  -}

scoresRecomputeTableNgramsHandler :: (IsDBCmd err env m, HasNodeStoryEnv err env)
                                  => NodeId -> TabType -> ListId -> m Int
scoresRecomputeTableNgramsHandler nId tabType listId = do
  env <- view hasNodeStory
  runDBQuery $ scoresRecomputeTableNgrams env nId tabType listId

scoresRecomputeTableNgrams :: NodeStoryEnv err -> NodeId -> TabType -> ListId -> DBQuery err x Int
scoresRecomputeTableNgrams env nId tabType listId = do
  tableMap <- getNgramsTableMap env listId ngramsType
  _ <- tableMap & v_data %%~ (setNgramsTableScores nId listId ngramsType)
                           . Map.mapWithKey ngramsElementFromRepo

  pure $ 1
  where
    ngramsType = ngramsTypeFromTabType tabType

getTableNgramsDocHandler :: (IsDBCmd err env m, HasNodeStoryEnv err env)
                         => DocId -> TabType
                         -> ListId -> Limit -> Maybe Offset
                         -> Maybe ListType
                         -> Maybe MinSize -> Maybe MaxSize
                         -> Maybe OrderBy
                         -> Maybe Text -- full text search
                         -> m (VersionedWithCount NgramsTable)
getTableNgramsDocHandler dId tabType listId limit_ offset listType minSize maxSize orderBy _mt = do
  env <- view hasNodeStory
  runDBQuery $ getTableNgramsDoc env dId tabType listId limit_ offset listType minSize maxSize orderBy _mt

-- | Text search is deactivated for now for ngrams by doc only
getTableNgramsDoc :: NodeStoryEnv err
                  -> DocId -> TabType
                  -> ListId -> Limit -> Maybe Offset
                  -> Maybe ListType
                  -> Maybe MinSize -> Maybe MaxSize
                  -> Maybe OrderBy
                  -> Maybe Text -- full text search
                  -> DBQuery err x (VersionedWithCount NgramsTable)
getTableNgramsDoc env 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 env dId listId tabType searchQuery


