import * as React from "react";
import type { StoreApi, UseBoundStore } from "zustand";
import { useSuffixContext } from "../contexts";
import { shallow } from "../utils";
import type { State } from "./useStore";

export const useConnectedEffect = (
  store: UseBoundStore<StoreApi<State>>,
  effect: (currentSlice: any, prevSlice: any) => () => void | undefined,
  ids: string[],
  deps: React.DependencyList = [],
) => {
  const suffix = useSuffixContext();
  const selector = React.useCallback(
    (currentData: State) => {
      return ids.map((id) => {
        const key = suffix ? `${id}-${suffix}` : id;
        return currentData[key];
      });
    },
    [ids, suffix],
  );
  const currentSlice = React.useRef(selector(store.getState()));
  const cleanup = React.useRef<() => void>(undefined);
  const listener = React.useCallback(() => {
    const nextSlice = selector(store.getState());
    if (!shallow(currentSlice.current, nextSlice)) {
      const prevSlice = currentSlice.current;
      if (currentSlice) {
        currentSlice.current = nextSlice;
      }
      if (cleanup) {
        cleanup.current?.();
        const result = effect(currentSlice.current, prevSlice);
        if (cleanup && typeof result === "function") {
          cleanup.current = result;
        }
      }
    }
  }, [selector, effect, store]);
  React.useEffect(() => {
    // if effect is an async function, then it will return a promise and we don't want to call cleanup
    const result = effect(currentSlice.current, currentSlice.current);
    if (cleanup && typeof result === "function") {
      cleanup.current = result;
    }
    const storeCleanup = store.subscribe(listener);
    return () => {
      cleanup?.current?.();
      storeCleanup?.();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps);

  // fn to manually re-trigger
  return React.useCallback(() => {
    cleanup?.current?.();
    const result = effect(currentSlice.current, currentSlice.current);
    if (cleanup && typeof result === "function") {
      cleanup.current = result;
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
};
