module Gargantext.Components.PhyloExplorer.Sidebar.DocList
  ( docListWrapper
  ) where

import Gargantext.Prelude

import Data.Array (concat)
import Data.Foldable (intercalate)
import Data.Maybe (Maybe(..))
import Data.Sequence as Seq
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (Variant(..))
import Gargantext.Components.FacetsTable (DocumentsView(..), Rows(..), initialPagePath, loadPage, publicationDate)
import Gargantext.Components.PhyloExplorer.Store as PhyloStore
import Gargantext.Components.PhyloExplorer.Types (CorpusId, DocId, FrameDoc(..), ListId)
import Gargantext.Components.RandomText (words)
import Gargantext.Components.Search (SearchQuery(..), SearchType(..))
import Gargantext.Config (defaultFrontends)
import Gargantext.Config.REST (RESTError(..))
import Gargantext.Ends (Frontends, url)
import Gargantext.Hooks.Loader (useLoaderEffect)
import Gargantext.Hooks.Session (useSession)
import Gargantext.Hooks.UpdateEffect (useUpdateEffect1')
import Gargantext.Routes as Routes
import Gargantext.Sessions (Session, sessionId)
import Gargantext.Utils (getter, (?))
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Toestand as T2
import Partial.Unsafe (unsafePartial)
import React.SyntheticEvent as SE
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T

here :: R2.Here
here = R2.here "Gargantext.Components.PhyloExplorer.Sidebar.DocList"


docListWrapper :: R2.Leaf ()
docListWrapper = R2.leaf docListWrapperCpt

docListWrapperCpt :: R.Component ()
docListWrapperCpt = here.component "wrapper" cpt where
  cpt _ _ = do
    -- | States
    -- |
    session <- useSession

    store <- PhyloStore.use

    extractedTerms <- R2.useLive' store.extractedTerms
    corpusId       <- R2.useLive' store.corpusId
    listId         <- R2.useLive' store.listId

    query' /\ query <- R2.useBox' Nothing

    -- | Helpers
    -- |
    let

      toSearchQuery items = SearchQuery
        { expected: SearchDoc
        , query: concat $ words <$> (getter _.label) <$> items
        }

    -- | Hooks
    -- |
    R.useEffect1' extractedTerms $
      T.write_ (extractedTerms # toSearchQuery >>> Just) query

    -- | Render
    -- |
    pure $

      R.fragment
      [
        case query' of

          Nothing ->
            B.caveat
            {}
            [
              H.text "You can link a corpus to retrieve relative documents about your selection"
            ]

          Just q' ->
            docList
            { query: q'
            , session
            , corpusId
            , listId
            , frameDoc: store.frameDoc
            , frontends: defaultFrontends
            }
      ]

-------------------------------------------------------------------

type ListProps =
  ( query           :: SearchQuery
  , corpusId        :: CorpusId
  , listId          :: ListId
  , session         :: Session
  , frameDoc        :: T.Box (Maybe FrameDoc)
  , frontends       :: Frontends
  )

docList :: R2.Leaf ListProps
docList = R2.leaf docListCpt

docListCpt :: R.Component ListProps
docListCpt = here.component "main" cpt where
  -- | Helpers
  -- |
  errorHandler err = do
    here.warn2 "[pageLayout] RESTError" err
    case err of
      ReadJSONError err' ->
        here.warn2 "[pageLayout] ReadJSONError" $ show err'
      _ -> pure unit
  -- | Component
  -- |
  cpt { query
      , session
      , corpusId: nodeId
      , listId
      , frameDoc
      , frontends
      } _ = do
    -- | States
    -- |

    path' /\ path
      <- R2.useBox' $ initialPagePath { nodeId, listId, query, session }

    state' /\ state <-
      R2.useBox' Nothing

    rows' /\ rows <-
      R2.useBox' Nothing

    frameDoc' <-
      R2.useLive' frameDoc

    -- | Hooks
    -- |

    useLoaderEffect
      { errorHandler
      , state
      , loader: loadPage
      , path: path'
      }

    -- | Effects
    -- |

    -- (on query change, reload fetched docs)
    useUpdateEffect1' query $
      flip T.write_ path $ initialPagePath { nodeId, listId, query, session }

    -- (on fetch success, extract existing docs)
    useUpdateEffect1' state' case state' of
      Nothing -> T.write_ (Just Seq.empty) rows
      Just r -> case r of
        Docs { docs } -> T.write_ (Just docs) rows
        _             -> T.write_ (Just Seq.empty) rows

    -- | Computed
    -- |
    let

      callback :: Maybe FrameDoc -> DocId -> Effect Unit
      callback
        Nothing
        new
          = setFrameDoc new # Just # flip T.write_ frameDoc

      callback
        (Just (FrameDoc { docId }))
        new
        | docId == new = T.write_ Nothing frameDoc
        | otherwise    = setFrameDoc new # Just # flip T.write_ frameDoc

      setFrameDoc :: DocId -> FrameDoc
      setFrameDoc docId = FrameDoc
        { docId
        , listId
        , corpusId: nodeId
        }

      isSelected :: Maybe FrameDoc -> DocumentsView -> Boolean
      isSelected
        (Just (FrameDoc { docId }))
        (DocumentsView { id })
          = docId == id

      isSelected
        _
        _
          = false

    -- | Render
    -- |
    pure $

      R2.fromMaybe rows' \results ->

        R.fragment
        [
          R2.when (results == Seq.empty) $

            B.caveat
            {}
            [
              H.text "No document found in your corpus for your selected terms"
            ]
        ,
          R2.when (not $ eq results Seq.empty) $

            H.ul
            { className: intercalate " "
                [ "phylo-doc-list"
                , "list-group"
                ]
            } $
            Seq.toUnfoldable $ flip Seq.map results \r ->

              item
              { documentView: (r :: DocumentsView)
              , callback: callback frameDoc'
              , isSelected: isSelected frameDoc' (r :: DocumentsView)
              , listId
              , corpusId: nodeId
              , session
              , frontends
              }
        ]


---------------------------------------------------------

type ItemProps =
  ( documentView :: DocumentsView
  , callback     :: DocId -> Effect Unit
  , isSelected   :: Boolean
  , corpusId     :: CorpusId
  , listId       :: ListId
  , session      :: Session
  , frontends    :: Frontends
  )

item :: R2.Leaf ItemProps
item = R2.leaf itemCpt

itemCpt :: R.Component ItemProps
itemCpt = here.component "item" cpt where
  cpt { documentView: dv@(DocumentsView { id, title, source })
      , callback
      , isSelected
      , listId
      , corpusId
      , session
      , frontends
      } _ = do
    -- Computed
    let
      -- Creating a href link
      route = Routes.CorpusDocument (sessionId session) corpusId listId id
      href = url frontends route

    -- Methods
    let
      onClick ::
           SE.SyntheticMouseEvent
        -> Effect Unit
      onClick event
          = R2.externalOpeningFlag event
        >>= case _ of
              true  -> R.nothing
              false -> SE.preventDefault event *> callback id

    -- Render
    pure $

      H.a
      { className: intercalate " "
          [ "phylo-doc-list__item"
          , isSelected ? "phylo-doc-list__item--selected" $ ""
          , "list-group-item"
          , "text-decoration-none"
          ]
      , on: { click: onClick }
      , href
      }
      [
        B.ripple
        { variant: Dark }
        [
          H.div
          { className: "phylo-doc-list__item__main" }
          [
            B.div'
            { className: "phylo-doc-list__item__title" }
            title
          ,
            B.div'
            { className: "phylo-doc-list__item__source" }
            source
          ,
            B.div'
            { className: "phylo-doc-list__item__date" } $
            publicationDate dv
          ]
        ,
          H.div
          { className: "phylo-doc-list__item__aside" }
          [
            B.icon
            { name: "eye-slash"
            , className: intercalate " "
                [ "text-info"
                , isSelected ? "visible" $ "hidden"
                ]
            }
          ]
        ]
      ]