{-|
Module      : Gargantext.Core.Worker.Jobs.Types
Description : Worker job definitions
Copyright   : (c) CNRS, 2024-Present
License     : AGPL + CECILL v3
Maintainer  : team@gargantext.org
Stability   : experimental
Portability : POSIX

-}

{-# LANGUAGE TypeApplications #-}

module Gargantext.Core.Worker.Jobs.Types where


import Data.Aeson ((.:), (.=), object, withObject)
import Data.Aeson.Types (prependFailure, typeMismatch)
import Data.Aeson qualified as JS
import Data.Aeson.KeyMap qualified as KM
import Data.Text qualified as T
import Gargantext.API.Admin.Auth.Types (AuthenticatedUser, ForgotPasswordAsyncParams)
import Gargantext.API.Ngrams.Types (NgramsList, UpdateTableNgramsCharts(_utn_list_id))
import Gargantext.API.Node.Contact.Types (AddContactParams)
import Gargantext.API.Node.Corpus.Annuaire (AnnuaireWithForm)
import Gargantext.API.Node.Document.Export.Types (Document)
import Gargantext.API.Node.DocumentsFromWriteNodes.Types qualified as DFWN
import Gargantext.API.Node.DocumentUpload.Types (DocumentUpload)
import Gargantext.API.Node.FrameCalcUpload.Types (FrameCalcUpload)
import Gargantext.API.Node.New.Types ( PostNode(..) )
import Gargantext.API.Node.Types (NewWithFile, NewWithTempFile, WithQuery(..))
import Gargantext.API.Node.Update.Types (UpdateNodeParams)
import Gargantext.Core.Types.Individu (User)
import Gargantext.Database.Admin.Types.Node (AnnuaireId, CorpusId, ListId, NodeId(UnsafeMkNodeId), ParentId)
import Gargantext.Prelude

data ImportRemoteTermsPayload
  = ImportRemoteTermsPayload
  { _irtp_list_id     :: ListId
  , _irtp_ngrams_list :: NgramsList
  } deriving (Show, Eq)

instance ToJSON ImportRemoteTermsPayload where
  toJSON ImportRemoteTermsPayload{..} =
    object [ "list_id"     .= _irtp_list_id
           , "ngrams_list" .= _irtp_ngrams_list
           ]

instance FromJSON ImportRemoteTermsPayload where
  parseJSON = withObject "ImportRemoteTermsPayload" $ \o -> do
    _irtp_list_id     <- o .: "list_id"
    _irtp_ngrams_list <- o .: "ngrams_list"
    pure ImportRemoteTermsPayload{..}

data WorkSplit
  = WorkSplit { _ws_current :: Int, _ws_total :: Int }
  deriving (Show, Eq)

instance ToJSON WorkSplit where
  toJSON WorkSplit{..} =
    object [ "current" .= _ws_current
           , "total"   .= _ws_total
           ]

instance FromJSON WorkSplit where
  parseJSON = withObject "WorkSplit" $ \o -> do
    _ws_current <- o .: "current"
    _ws_total   <- o .: "total"
    pure WorkSplit{..}

data ImportRemoteDocumentsPayload
  = ImportRemoteDocumentsPayload
  { _irdp_user :: AuthenticatedUser
  , _irdp_parent_id :: ParentId
  , _irdp_corpus_id :: NodeId
  , _irdp_documents :: [Document]
  -- | Useful to compute total progress in logs.
  , _irdp_work_split :: WorkSplit
  } deriving (Show, Eq)

instance ToJSON ImportRemoteDocumentsPayload where
  toJSON ImportRemoteDocumentsPayload{..} =
    object [ "user" .= _irdp_user
           , "corpus_id" .= _irdp_corpus_id
           , "parent_id" .= _irdp_parent_id
           , "documents" .= _irdp_documents
           , "work_split" .= _irdp_work_split
           ]

instance FromJSON ImportRemoteDocumentsPayload where
  parseJSON = withObject "ImportRemoteDocumentsPayload" $ \o -> do
    _irdp_user <- o .: "user"
    _irdp_parent_id <- o .: "parent_id"
    _irdp_corpus_id <- o .: "corpus_id"
    _irdp_documents <- o .: "documents"
    _irdp_work_split <- o .: "work_split"
    pure ImportRemoteDocumentsPayload{..}

data Job =
    Ping
  | AddContact { _ac_args :: AddContactParams
               , _ac_node_id :: NodeId
               , _ac_user :: User }
  | AddCorpusTempFileAsync { _actf_args :: NewWithTempFile
                           , _actf_user :: User
                           , _actf_cid  :: CorpusId }  
  | AddCorpusWithQuery { _acq_args :: WithQuery
                       , _acq_user :: User
                       , _acq_cid   :: CorpusId }
  | AddWithFile { _awf_args :: NewWithFile
                , _awf_authenticatedUser :: AuthenticatedUser
                , _awf_node_id :: NodeId }  
  | AddToAnnuaireWithForm { _aawf_annuaire_id :: AnnuaireId
                          , _aawf_args :: AnnuaireWithForm }
  | DocumentsFromWriteNodes { _dfwn_args :: DFWN.Params
                            , _dfwn_authenticatedUser :: AuthenticatedUser
                            , _dfwn_node_id :: NodeId }
  | ForgotPasswordAsync { _fpa_args :: ForgotPasswordAsyncParams }
  | FrameCalcUpload { _fca_args :: FrameCalcUpload
                    , _fca_authenticatedUser :: AuthenticatedUser
                    , _fca_node_id :: NodeId }
  | JSONPost { _jp_list_id :: ListId
             , _jp_ngrams_oid :: Int }
             -- , _jp_ngrams_list :: NgramsList }
  | NgramsPostCharts { _npc_node_id :: NodeId
                     , _npc_args    :: UpdateTableNgramsCharts }
  | PostNodeAsync { _pna_node_id :: NodeId
                  , _pna_authenticatedUser :: AuthenticatedUser
                  , _pna_args :: PostNode }
  | RecomputeGraph { _rg_node_id :: NodeId }
  | UpdateNode { _un_node_id :: NodeId
               , _un_args    :: UpdateNodeParams }
  | UploadDocument { _ud_node_id :: NodeId
                   , _ud_args    :: DocumentUpload }
  | ImportRemoteDocuments !ImportRemoteDocumentsPayload
  | ImportRemoteTerms     !ImportRemoteTermsPayload
  deriving (Show, Eq)
instance FromJSON Job where
  parseJSON = withObject "Job" $ \o -> do
    type_ <- o .: "type"
    case type_ of
      "Ping" -> return Ping
      "AddContact" -> do
        _ac_args <- o .: "args"
        _ac_node_id <- o .: "node_id"
        _ac_user <- o .: "user"
        return $ AddContact { .. }
      "AddCorpusTempFileAsync" -> do
        _actf_args <- o .: "args"
        _actf_user <- o .: "user"
        _actf_cid <- o .: "cid"
        return $ AddCorpusTempFileAsync { .. }
      "AddCorpusWithQuery" -> do
        _acq_args <- o .: "args"
        _acq_user <- o .: "user"
        _acq_cid <- o .: "cid"
        return $ AddCorpusWithQuery { .. }
      "AddToAnnuaireWithForm" -> do
        _aawf_args <- o .: "args"
        _aawf_annuaire_id <- o .: "annuaire_id"
        return $ AddToAnnuaireWithForm { .. }
      "AddWithFile" -> do
        _awf_args <- o .: "args"
        _awf_authenticatedUser <- o .: "authenticated_user"
        _awf_node_id <- o .: "node_id"
        return $ AddWithFile { .. }
      "DocumentsFromWriteNodes" -> do
        _dfwn_args <- o .: "args"
        _dfwn_authenticatedUser <- o .: "authenticated_user"
        _dfwn_node_id <- o .: "node_id"
        return $ DocumentsFromWriteNodes { .. }
      "ForgotPasswordAsync" -> do
        _fpa_args <- o .: "args"
        return $ ForgotPasswordAsync { .. }
      "FrameCalcUpload" -> do
        _fca_args <- o .: "args"
        _fca_authenticatedUser <- o .: "authenticated_user"
        _fca_node_id <- o .: "node_id"
        return $ FrameCalcUpload { .. }
      "JSONPost" -> do
        _jp_list_id <- o .: "list_id"
        -- _jp_ngrams_list <- o .: "ngrams_list"
        _jp_ngrams_oid <- o .: "ngrams_oid"
        return $ JSONPost { .. }
      "NgramsPostCharts" -> do
        _npc_node_id <- o .: "node_id"
        _npc_args <- o .: "args"
        return $ NgramsPostCharts { .. }
      "PostNodeAsync" -> do
        _pna_node_id <- o .: "node_id"
        _pna_authenticatedUser <- o .: "authenticated_user"
        _pna_args <- o .: "args"
        return $ PostNodeAsync { .. }
      "RecomputeGraph" -> do
        _rg_node_id <- o .: "node_id"
        return $ RecomputeGraph { .. }
      "UpdateNode" -> do
        _un_node_id <- o .: "node_id"
        _un_args <- o .: "args"
        return $ UpdateNode { .. }
      "UploadDocument" -> do
        _ud_node_id <- o .: "node_id"
        _ud_args <- o .: "args"
        return $ UploadDocument { .. }
      "ImportRemoteDocuments" ->
        ImportRemoteDocuments <$> parseJSON (JS.Object o)
      "ImportRemoteTerms" ->
        ImportRemoteTerms <$> parseJSON (JS.Object o)
      s -> prependFailure "parsing job type failed, " (typeMismatch "type" s)
instance ToJSON Job where
  toJSON Ping = object [ "type" .= ("Ping" :: Text) ]
  toJSON (AddContact { .. }) =
    object [ "type" .= ("AddContact" :: Text)
           , "args" .= _ac_args
           , "user" .= _ac_user
           , "node_id" .= _ac_node_id ]
  toJSON (AddCorpusTempFileAsync { .. }) =
    object [ "type" .= ("AddCorpusTempFileAsync" :: Text)
           , "args" .= _actf_args
           , "user" .= _actf_user
           , "cid" .= _actf_cid ]
  toJSON (AddCorpusWithQuery { .. }) =
    object [ "type" .= ("AddCorpusWithQuery" :: Text)
           , "args" .= _acq_args
           , "user" .= _acq_user
           , "cid" .= _acq_cid ]
  toJSON (AddToAnnuaireWithForm { .. }) =
    object [ "type" .= ("AddToAnnuaireWithForm" :: Text)
           , "args" .= _aawf_args
           , "annuaire_id" .= _aawf_annuaire_id ]
  toJSON (AddWithFile { .. }) =
    object [ "type" .= ("AddWithFile" :: Text)
           , "args" .= _awf_args
           , "authenticated_user" .= _awf_authenticatedUser
           , "node_id" .= _awf_node_id ]
  toJSON (DocumentsFromWriteNodes { .. }) =
    object [ "type" .= ("DocumentsFromWriteNodes" :: Text)
           , "args" .= _dfwn_args
           , "authenticated_user" .= _dfwn_authenticatedUser
           , "node_id" .= _dfwn_node_id ]
  toJSON (ForgotPasswordAsync { .. }) =
    object [ "type" .= ("ForgotPasswordAsync" :: Text)
           , "args" .= _fpa_args ]
  toJSON (FrameCalcUpload { .. }) =
    object [ "type" .= ("FrameCalcUpload" :: Text)
           , "args" .= _fca_args
           , "authenticated_user" .= _fca_authenticatedUser
           , "node_id" .= _fca_node_id ]
  toJSON (JSONPost { .. }) =
    object [ "type" .= ("JSONPost" :: Text)
           , "list_id" .= _jp_list_id
           , "ngrams_oid" .= _jp_ngrams_oid ]
           -- , "ngrams_list" .= _jp_ngrams_list ]
  toJSON (NgramsPostCharts { .. }) =
    object [ "type" .= ("NgramsPostCharts" :: Text)
           , "node_id" .= _npc_node_id
           , "args" .= _npc_args ]
  toJSON (PostNodeAsync { .. }) =
    object [ "type" .= ("PostNodeAsync" :: Text)
           , "node_id" .= _pna_node_id
           , "authenticated_user" .= _pna_authenticatedUser
           , "args" .= _pna_args ]
  toJSON (RecomputeGraph { .. }) =
    object [ "type" .= ("RecomputeGraph" :: Text)
           , "node_id" .= _rg_node_id ]
  toJSON (UpdateNode { .. }) =
    object [ "type" .= ("UpdateNode" :: Text)
           , "node_id" .= _un_node_id
           , "args" .= _un_args ]
  toJSON (UploadDocument { .. }) =
    object [ "type" .= ("UploadDocument" :: Text)
           , "node_id" .= _ud_node_id
           , "args" .= _ud_args ]
  toJSON (ImportRemoteDocuments payload) =
    case toJSON payload of
      (JS.Object o) ->
        let o1 = KM.fromList [ ("type", toJSON @T.Text "ImportRemoteDocuments") ]
        in JS.Object $ o1 <> o
      _ -> errorTrace "impossible, toJSON ImportRemoteDocuments did not return an Object."
  toJSON (ImportRemoteTerms payload) =
    case toJSON payload of
      (JS.Object o) ->
        let o1 = KM.fromList [ ("type", toJSON @T.Text "ImportRemoteTerms") ]
        in JS.Object $ o1 <> o
      _ -> errorTrace "impossible, toJSON ImportRemoteTerms did not return an Object."

-- | We want to have a way to specify 'Maybe NodeId' from given worker
-- parameters. The given 'Maybe CorpusId' is an alternative, when
-- params don't have node id access.
-- class HasWorkerNodeId input where
--   getMNodeId :: job -> Maybe CorpusId -> Maybe NodeId

getWorkerMNodeId :: Job -> Maybe NodeId
getWorkerMNodeId Ping = Nothing
getWorkerMNodeId (AddContact { _ac_node_id }) = Just _ac_node_id
getWorkerMNodeId (AddCorpusTempFileAsync { _actf_cid }) = Just _actf_cid
getWorkerMNodeId (AddCorpusWithQuery { _acq_args = WithQuery { _wq_node_id }}) = Just $ UnsafeMkNodeId _wq_node_id
getWorkerMNodeId (AddToAnnuaireWithForm { _aawf_annuaire_id }) = Just _aawf_annuaire_id
getWorkerMNodeId (AddWithFile { _awf_node_id }) = Just _awf_node_id
getWorkerMNodeId (DocumentsFromWriteNodes { _dfwn_node_id }) = Just _dfwn_node_id
getWorkerMNodeId (ForgotPasswordAsync {}) = Nothing
getWorkerMNodeId (FrameCalcUpload { _fca_node_id }) = Just _fca_node_id
getWorkerMNodeId (JSONPost { _jp_list_id }) = Just _jp_list_id
getWorkerMNodeId (NgramsPostCharts { _npc_args }) = Just $ _utn_list_id _npc_args
getWorkerMNodeId (PostNodeAsync { _pna_node_id }) = Just _pna_node_id
getWorkerMNodeId (RecomputeGraph { _rg_node_id }) = Just _rg_node_id
getWorkerMNodeId (UpdateNode { _un_node_id }) = Just _un_node_id
getWorkerMNodeId (UploadDocument { _ud_node_id }) = Just _ud_node_id
getWorkerMNodeId (ImportRemoteDocuments (ImportRemoteDocumentsPayload _ _ corpusId _ _)) = Just corpusId
getWorkerMNodeId (ImportRemoteTerms (ImportRemoteTermsPayload listId _)) = Just listId
