module Gargantext.Context.Progress
  ( AsyncProps
  , asyncProgress
  , asyncContext
  ) where

import Gargantext.Prelude

import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Effect.Timer (IntervalId, clearInterval, setInterval)
import Gargantext.Components.Forest.Tree.Node.Tools.ProgressBar (QueryProgressData, queryProgress)
import Gargantext.Config.Utils (handleErrorInAsyncProgress, handleRESTError)
import Gargantext.Hooks.FirstEffect (useFirstEffect')
import Gargantext.Sessions (Session)
import Gargantext.Types (AsyncProgress, FrontendError)
import Gargantext.Types as GT
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Record.Extra as RX
import Toestand as T

type AsyncProps =
  ( asyncTask :: GT.AsyncTaskWithType
  , errors    :: T.Box (Array FrontendError)
  , nodeId    :: GT.ID
  , onFinish  :: Unit -> Effect Unit
  , session   :: Session
  )

asyncProgress :: R2.Component AsyncProps
asyncProgress = R2.component component

component :: R.Component AsyncProps
component = R.hooksComponent "asyncProgressContext" cpt where
  cpt props@{ errors
            , onFinish
            } children = do
    -- States
    progress /\ progressBox <- R2.useBox' 0.0
    intervalIdRef <- R.useRef (Nothing :: Maybe IntervalId)

    -- Methods
    let
      exec :: Unit -> Effect Unit
      exec _ = launchAff_ do
        let rdata = (RX.pick props :: Record QueryProgressData)

        eAsyncProgress <- queryProgress rdata
        handleRESTError errors eAsyncProgress onProgress

      onProgress :: AsyncProgress -> Aff Unit
      onProgress value = liftEffect do
        let GT.AsyncProgress { status } = value

        T.write_ (min 100.0 $ GT.progressPercent value) progressBox

        if (status == GT.IsFinished) ||
           (status == GT.IsKilled) ||
           (status == GT.IsFailure)
        then do
          case R.readRef intervalIdRef of
            Nothing  -> R.nothing
            Just iid -> clearInterval iid
          handleErrorInAsyncProgress errors value
          onFinish unit
        else
          R.nothing

    -- Hooks
    useFirstEffect' do
      intervalId <- setInterval 1000 $ exec unit
      R.setRef intervalIdRef $ Just intervalId

    -- Render
    pure $

      R.provideContext asyncContext (progress)
      children

asyncContext :: R.Context (Number)
asyncContext = R.createContext 0.0