module Gargantext.Components.TreeSearch where

import Gargantext.Prelude

import Data.Array (head)
import Data.Maybe (Maybe(..), fromMaybe)
import Effect (Effect)
import Gargantext.Components.Bootstrap (formSelect')
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ModalSizing(..), Position(..), TooltipPosition(..), Variant(..))
import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Config.REST (AffRESTError, logRESTError)
import Gargantext.Hooks.LinkHandler (useLinkHandler)
import Gargantext.Hooks.Loader (useLoader)
import Gargantext.Routes (AppRoute(..), appPath, nodeTypeAppRoute)
import Gargantext.Sessions (Session(..), Sessions, get, sessionId, unSessions)
import Gargantext.Sessions.Types (Session)
import Gargantext.Types (NodeID, NodeType, getIcon)
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T

here :: R2.Here
here = R2.here "Gargantext.Components.TreeSearch"

type Props = (
  visible :: T.Box Boolean
, sessions :: T.Box Sessions
)

type StateProps = (
  visible :: T.Box Boolean
, sessions :: Sessions
, query :: T.Box String
)

type WrapperProps = (
  visible :: T.Box Boolean
, sessions :: Sessions
, session  :: T.Box (Maybe Session)
, query :: T.Box String
)

type ContainerProps = ( visible :: T.Box Boolean, session :: Session, query :: String)

type RenderContainerProps = ( visible :: T.Box Boolean, session :: Session, searchData :: Array SearchData )

type RenderProps = ( visible        :: T.Box Boolean
                   , session        :: Session
                   , searchData     :: Array SearchData
                   , goToRoute      :: AppRoute -> Effect Unit )

type SearchData = { name :: String
                  , type :: NodeType
                  , id   :: NodeID
}

treeSearch :: R2.Leaf Props
treeSearch = R2.leaf treeSearchCpt

treeSearchCpt :: R.Component Props
treeSearchCpt = here.component "treeSearch" cpt where
  cpt { sessions, visible } _ = do
    sessions' <- T.useLive T.unequal sessions
    query <- T.useBox ""
    inputRef <- R.useRef ""

    pure $ 
      B.baseModal
      { isVisibleBox: visible
      , title: Just "Search in tree"
      , size: MediumModalSize
      , modalClassName: ""
      }
      [ 
        H.div
        { className: "input-group p-1" }
        [
          inputWithEnter { className: "form-control"
                       , autoFocus: true
                       , onEnter: submit inputRef query
                       , onValueChanged: R.setRef inputRef
                       , onBlur: R.setRef inputRef
                       , type: "value"
                       , defaultValue: ""
                       , required: true
                       , placeholder: "Search keys..."
                       }
        ,
          H.div { className: "input-group-append"}
          [
            H.button { className: "input-group-text btn btn-light text-secondary"
                    , on: { click: submit inputRef query }
                    , type: "submit" }
            [ H.span {className: "fa fa-search"} [] ]
          ]
        ]
      , treeSearchState {visible, query, sessions: sessions'} 
      ]
      where
        submit ref box _ = T.write_ (R.readRef ref) box

treeSearchState :: R2.Leaf StateProps
treeSearchState = R2.leaf treeSearchStateCpt

treeSearchStateCpt :: R.Component StateProps
treeSearchStateCpt = here.component "treeSearchState" cpt where
  cpt { query, sessions, visible } _ = do
    session <- T.useBox $ head $ unSessions sessions

    pure $ treeSearchWrapper { query, visible, session, sessions}

treeSearchWrapper :: R2.Leaf WrapperProps
treeSearchWrapper = R2.leaf treeSearchWrapperCpt

treeSearchWrapperCpt :: R.Component WrapperProps
treeSearchWrapperCpt = here.component "treeSearchWrapper" cpt where
  cpt { query, visible, sessions, session } _ = do
    session' <- T.useLive T.unequal session
    query' <- T.useLive T.unequal query

    case session' of
      Just s ->
        pure $ H.div {className: "search-modal__results-wrapper px-1"} 
        [ formSelect'
            { callback: \v -> T.write_ (Just v) session
            , list: unSessions sessions
            , value: s
            } []
        ,
          H.hr {}
        , 
          if query' == "" then
            H.div {className: "search-modal__results mb-1"} [B.span_ "Please enter your search query..."]
          else
            treeSearchContainer {query: query', visible, session: s}
        ]
      Nothing -> pure $ H.div {} []

treeSearchContainer :: R2.Leaf ContainerProps
treeSearchContainer = R2.leaf treeSearchContainerCpt

treeSearchContainerCpt :: R.Component ContainerProps
treeSearchContainerCpt = here.component "treeSearchContainerCpt" cpt where
  cpt {query, visible, session } _ = do

    useLoader { errorHandler
              , path: { session, query }
              , loader: loadSearch
              , render: \searchData -> treeSearchRenderContainer { visible, session, searchData }
    }
    where
      errorHandler = logRESTError here "[treeSearchContainer]"

treeSearchRenderContainer :: R2.Leaf RenderContainerProps
treeSearchRenderContainer = R2.leaf treeSearchRenderContainerCpt

treeSearchRenderContainerCpt :: R.Component RenderContainerProps
treeSearchRenderContainerCpt = here.component "treeSearchRenderContainer" cpt where
  cpt { visible, session, searchData } _ = do
    { goToRoute } <- useLinkHandler
    pure $ treeSearchRender { visible, session, searchData, goToRoute }

treeSearchRender :: R2.Leaf RenderProps
treeSearchRender = R2.leaf treeSearchRenderCpt

treeSearchRenderCpt :: R.Component RenderProps
treeSearchRenderCpt = here.component "treeSearchRenderCpt" cpt where
  cpt { visible, session, searchData, goToRoute } _ = do

    pure $ H.div {className: "search-modal__results"} (results searchData)
      where
        results s = map searchResult s
          where
            searchResult sd = H.div 
                                { className: "result mt-1"} 
                                [ 
                                  B.tooltipContainer
                                  { delayShow: 600
                                  , position: TooltipPosition Right
                                  , tooltipSlot: B.span_ $ show $ fromMaybe Home $ nodeTypeAppRoute sd.type (sessionId session) sd.id
                                  , defaultSlot:
                                    B.button 
                                    { className: "result__button"
                                    , callback: \_ -> do 
                                      T.write_ false visible
                                      goToRoute $ fromMaybe Home $ nodeTypeAppRoute sd.type (sessionId session) sd.id 
                                    , variant: ButtonVariant Light }
                                    [
                                      B.icon {name: getIcon sd.type true}
                                    , B.wad_ [ "d-inline-block", "w-1" ] 
                                    , H.text sd.name
                                    ]
                                  }
                                ]

type LoadProps = ( session :: Session, query :: String )

loadSearch :: Record LoadProps -> AffRESTError (Array SearchData)
loadSearch { session: s, query: q} = get s $ appPath (TreeFlat (sessionId s) (sessionRoot s) q)
  where sessionRoot (Session {treeId}) = treeId