{-# LANGUAGE BangPatterns        #-}
{-# LANGUAGE QuasiQuotes         #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}
{-# LANGUAGE TypeFamilies        #-}

module Test.API.Private.List (
    tests
  ) where

import Data.Aeson.QQ
import Data.Text qualified as T
import Data.Text.IO qualified as TIO
import Fmt
import Gargantext.API.Admin.Orchestrator.Types
import Gargantext.API.HashedResponse
import Gargantext.API.Ngrams.List.Types
import Gargantext.API.Ngrams.Types qualified as APINgrams
import Gargantext.API.Node.Corpus.New.Types qualified as FType
import Gargantext.API.Node.Types
import Gargantext.Core qualified as Lang
import Gargantext.Core.Config
import Gargantext.Core.Text.List.Social
import Gargantext.Core.Types
import Gargantext.Core.Types.Individu
import Gargantext.Prelude
import Paths_gargantext
import Prelude
import Servant.Client.Streaming
import Test.API.Prelude (newCorpusForUser, checkEither)
import Test.API.Routes
import Test.API.Setup
import Test.Database.Types
import Test.Hspec (Spec, it, aroundAll, describe, sequential)
import Test.Hspec.Expectations
import Test.Hspec.Wai.Internal (withApplication)
import Test.Utils

mkNewWithForm :: T.Text -> T.Text -> NewWithForm
mkNewWithForm content name = NewWithForm
  { _wf_filetype   = FType.TSV
  , _wf_fileformat = FType.Plain
  , _wf_data       = content
  , _wf_lang       = Just Lang.EN
  , _wf_name       = name
  , _wf_selection  = FlowSocialListWithPriority MySelfFirst
  }


importTermsTSV :: SpecContext () -> String -> IO (JobLog, CorpusId, ListId)
importTermsTSV (SpecContext testEnv port app _) name = do
  cId <- liftIO $ newCorpusForUser testEnv "alice"
  let log_cfg = test_config testEnv ^. gc_logging
  withApplication app $ do
    withValidLogin port "alice" (GargPassword "alice") $ \clientEnv token -> do
      ([listId] :: [NodeId]) <- protectedJSON token "POST" (mkUrl port ("/node/" <> build cId)) [aesonQQ|{"pn_typename":"NodeList","pn_name":"Testing"}|]
      -- Upload the CSV doc
      simpleNgrams <- liftIO (TIO.readFile =<< getDataFileName name)
      let params = WithTextFile { _wtf_filetype = FType.TSV
                                , _wtf_data = simpleNgrams
                                , _wtf_name = "simple.tsv" }
      pendingJob <- checkEither $ liftIO $ runClientM (add_tsv_to_list token listId params) clientEnv
      jobLog <- pollUntilWorkFinished log_cfg port pendingJob

      pure (jobLog, cId, listId)

importCorpusTSV :: SpecContext () -> String -> IO (JobLog, CorpusId, ListId)
importCorpusTSV (SpecContext testEnv port app _) name = do
  cId <- liftIO $ newCorpusForUser testEnv "alice"
  let log_cfg = test_config testEnv ^. gc_logging
  withApplication app $ do
    withValidLogin port "alice" (GargPassword "alice") $ \clientEnv token -> do
      ([listId] :: [NodeId]) <- protectedJSON token "POST" (mkUrl port ("/node/" <> build cId)) [aesonQQ|{"pn_typename":"NodeList","pn_name":"Testing"}|]
      -- Upload the CSV doc
      simpleNgrams <- liftIO (TIO.readFile =<< getDataFileName name)
      let params = mkNewWithForm simpleNgrams "simple.tsv"
      pendingJob <- checkEither $ liftIO $ runClientM (importCorpus token cId params) clientEnv
      jobLog <- pollUntilWorkFinished log_cfg port pendingJob

      pure (jobLog, cId, listId)

tests :: Spec
tests = sequential $ aroundAll withTestDBAndPort $ do
  describe "Prelude" $ do
    it "setup DB triggers" $ \SpecContext{..} -> do
      setupEnvironment _sctx_env
      -- Let's create the Alice user.
      void $ createAliceAndBob _sctx_env

    describe "Importing terms as TSV" $ do

      it "should work for TSV with a missing 'forms' column" $ \ctx@(SpecContext _ port app _) -> do
        (_, cId, listId) <- importTermsTSV ctx "test-data/issue-381/Termes_A_Ajouter_T4SC_Intellixir.tsv"
        withApplication app $ do
          withValidLogin port "alice" (GargPassword "alice") $ \clientEnv token -> do
            -- Now check that we can retrieve the ngrams, and the ngrams list is not empty!
            liftIO $ do
              eRes <- checkEither $ runClientM (get_table_ngrams token cId APINgrams.Terms listId 50 Nothing (Just MapTerm) Nothing Nothing Nothing Nothing) clientEnv
              let (APINgrams.NgramsTable terms) = APINgrams._vc_data eRes
              length terms `shouldSatisfy` (>= 1)

      it "should handle dirty TSV as per issue #380" $ \ctx@(SpecContext _testEnv port app _) -> do
        (_, cId, _listId) <- importCorpusTSV ctx "test-data/issue-380/corpus.tsv"
        withApplication app $ do
          withValidLogin port "alice" (GargPassword "alice") $ \clientEnv token -> do
            -- Now check that we can retrieve the ngrams, and the ngrams list is not empty!
            liftIO $ do
              eRes <- checkEither $ runClientM (get_table token cId (Just APINgrams.Docs) Nothing Nothing Nothing Nothing Nothing) clientEnv
              let (HashedResponse _ TableResult{tr_docs}) = eRes
              length tr_docs `shouldBe` 7

      it "should skip problematic rows" $ \ctx@(SpecContext _testEnv port app _) -> do
        (jobLogs, cId, _listId) <- importCorpusTSV ctx "test-data/issue-380/malformed_row.tsv"
        withApplication app $ do
          withValidLogin port "alice" (GargPassword "alice") $ \clientEnv token -> do
            -- Now check that we can retrieve the ngrams, and the ngrams list is not empty!
            liftIO $ do
              eRes <- checkEither $ runClientM (get_table token cId (Just APINgrams.Docs) Nothing Nothing Nothing Nothing Nothing) clientEnv
              let (HashedResponse _ TableResult{tr_docs}) = eRes
              length tr_docs `shouldBe` 6 -- it must have skipped the broken row
              -- Check that the events include the two failures we encountered.
              _scst_events jobLogs `shouldBe` Just [
                  ScraperEvent {
                    _scev_message = Just "Skipping record at row 6 as parsing failed due to: no field named \"Publication Year\""
                  , _scev_level = Just "WARNING"
                  , _scev_date = Nothing
                }
                ,ScraperEvent {
                    _scev_message = Just "Skipping record at row 8 as parsing failed due to: parse error (endOfInput)"
                  , _scev_level = Just "WARNING"
                  , _scev_date = Nothing
                }
                ]
