import React, { createContext, useContext, useReducer } from "react";

// 1️⃣
const initialState = {
  containerRef: null,
  stickyRefs: new Map(),
};

// No operation
const noop = () => {};
// 2️⃣
const initialDispatch = {
  setContainerRef: noop,
  addStickyRef: noop
};

const StickyStateContext = createContext(initialState);
const StickyDispatchContext = createContext(initialDispatch);


const ActionType = {
  setContainerRef: "set container ref",
  addStickyRef: "add sticky ref",
};

// 3️⃣
function reducer(state, action) {
  const { type, payload } = action;
  switch (type) {
    case ActionType.setContainerRef:
      // Reassigning a new ref, will infinitely re-load!
      return Object.assign(state, {
        containerRef: { current: payload.containerRef }
      });
    case ActionType.addStickyRef:
      const { topSentinelRef, bottomSentinelRef, stickyRef } = payload;

      state.stickyRefs.set(topSentinelRef.current, stickyRef);
      state.stickyRefs.set(bottomSentinelRef.current, stickyRef);

      return Object.assign(state, {
        stickyRefs: state.stickyRefs
      });
    default:
      return state;
  }
}
// 4️⃣
function StickyProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);

  const setContainerRef = containerRef =>
    dispatch({ type: ActionType.setContainerRef, payload: { containerRef } });
  const addStickyRef = (topSentinelRef, bottomSentinelRef, stickyRef) =>
    dispatch({
      type: ActionType.addStickyRef,
      payload: { topSentinelRef, bottomSentinelRef, stickyRef }
    });

  // 5️⃣
  const actions = { setContainerRef, addStickyRef };

  return (
    <StickyStateContext.Provider value={state}>
      <StickyDispatchContext.Provider value={actions}>
        {children}
      </StickyDispatchContext.Provider>
    </StickyStateContext.Provider>
  );
}

// 6️⃣
function useStickyState() {
  const context = useContext(StickyStateContext);
  if (context === undefined)
    throw Error('"useStickyState should be used under "StickyStateContext');
  return context;
}

// 7️⃣
function useStickyActions() {
  const context = useContext(StickyDispatchContext);
  if (context === undefined)
    throw Error(
      '"useStickyActions should be used under "StickyDispatchContext'
    );
  return context;
}

const initialSectionValues = {
  topSentinelRef: null,
  bottomSentinelRef: null
};

// 8️⃣
const StickySectionContext = createContext(initialSectionValues);

export {
  StickyProvider,
  useStickyState,
  useStickyActions,
  ActionType,
  StickySectionContext
};