module Gargantext.Utils.Stores
  ( createStore
  , provideStore
  , useStore
  ) where

import Gargantext.Prelude

import Prim.RowList (class RowToList)
import Reactix as R
import Toestand as T
import Toestand.Records as TR

-- StoreFactory: R.Hook (Record Store) → create focus boxes, hydrate box values
-- StoreProvider: R.Hooks (Record Store) → provide context with previous boxes
--    ↓
-- StoreHook: R.Hooks (Record Store) → use context

-- | From state values to focused boxes
createStore :: forall boxes l state.
     RowToList state l
  => TR.UseFocusedFields l boxes () (T.Box (Record state))
  => Record state
  -> R.Hooks (Record boxes)
createStore = T.useBox >=> flip T.useFocusedFields {}

-- | Set <Store.Provider> bearing Stores as a Context
-- |
-- | (!) do not use store binds in this specific component (eg. `useStores`)
provideStore :: forall boxes l state.
     RowToList state l
  => TR.UseFocusedFields l boxes () (T.Box (Record state))
  => String
  -> Record state
  -> R.Context (Record boxes)
  -> Array R.Element
  -> R.Element
provideStore name state context
  = R.createElement (R.hooksComponent name cpt) {}
    where
      cpt _ children = do
        store <- createStore state
        pure $ R.provideContext context store $ children

-- | (?) As we use "React Provide API", we just want to rely on its Global
-- |     Reference (and not as Mutable State thanks to "Consumer API")
-- |     Hence the "unsafeCoerce" used on every provided context, avoiding
-- |     unwanted computing at each import
-- |
-- |     It also implies that every call to the proxy reference (made thanks to
-- |     below `<Store.Provider>` are made AFTER first mount of this very,
-- |     component, otherwise, every call will return the empty `unit`)
useStore :: forall boxes.
     R.Context (Record boxes)
  -> R.Hooks (Record boxes)
useStore = R.useContext