module Gargantext.Components.TreeSearch where

import Gargantext.Prelude

import Data.Array (head)
import Data.Map as Map
import Data.Maybe (Maybe(..), fromMaybe)
import Effect (Effect)
import Gargantext.Components.App.Store (Boxes)
import Gargantext.Components.App.Store as AppStore
import Gargantext.Components.Bootstrap (formSelect')
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (ButtonVariant(..), ModalSizing(..), Position(..), TooltipPosition(..), Variant(..))
import Gargantext.Components.Forest.Breadcrumb (breadcrumbView)
import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Config.REST (AffRESTError)
import Gargantext.Ends (class ToUrl, Backend(..), backendUrl, sessionPath)
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 (ApiVersion(..), 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: "modal-lg"
      }
      [ 
        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 = R2.hereComponent here "treeSearchContainerCpt" hCpt where
  hCpt hp {query, visible, session } _ = do

    useLoader { errorHandler: Nothing
              , herePrefix: hp
              , path: { session, query }
              , loader: loadSearch
              , render: \searchData -> treeSearchRenderContainer { visible, session, searchData }
    }

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 py-1"} 
                              [ 
                                B.tooltipContainer
                                { delayShow: 600
                                , position: TooltipPosition Right
                                -- , tooltipSlot: B.span_ $ show $ fromMaybe Home $ nodeTypeAppRoute sd.type (sessionId session) sd.id
                                , tooltipSlot: H.div {} 
                                               [
                                                 H.div {} 
                                                 [
                                                   H.text $ "Type: " <> show sd.type
                                                 , H.text $ " | Id: " <> show sd.id
                                                 ]
                                               , H.div {}
                                                 [
                                                   H.text " Path: "
                                                 , breadcrumbView { format: "text"
                                                                  , route: getRouteFromSearchResult sd session
                                                                  , session
                                                                  , openTreeNodes: false
                                                                  }
                                                 ]
                                               ]
                                , defaultSlot:
                                  B.button 
                                  { className: "result__button"
                                  , callback: \_ -> do 
                                    T.write_ false visible
                                    goToRoute $ getRouteFromSearchResult sd session 
                                  , variant: ButtonVariant Light }
                                  [
                                    B.icon {name: getIcon sd.type true}
                                  , B.wad_ [ "d-inline-block", "w-1" ] 
                                  , H.text sd.name
                                  ]
                                }
                              ,
                                H.span { className: "node-path small" }
                                [
                                  H.text " — "
                                , breadcrumbView { format: "text"
                                                 , route: getRouteFromSearchResult sd session
                                                 , session: session
                                                 , openTreeNodes: false
                                                 }
                                ]
                              ]

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

getRouteFromSearchResult :: SearchData -> Session -> AppRoute
getRouteFromSearchResult sd session = fromMaybe Home $ nodeTypeAppRoute sd.type (sessionId session) sd.id