import * as React from "react";
import { useRouteLoaderData } from "react-router";

type LoaderData = Record<string, any> & { tags?: string[] };

export function searchAndPatch(
  data: Record<string, any>,
  type: string,
  id: string,
  set: Record<string, any>,
) {
  if (!data) {
    return false;
  }
  if (Array.isArray(data)) {
    let didUpdate = false;
    data.forEach((item) => {
      const du = searchAndPatch(item, type, id, set);
      if (du) {
        didUpdate = true;
      }
    });
    return didUpdate;
  } else if (typeof data === "object") {
    if (data.id === id && data.__typename === type) {
      Object.assign(data, set);
      return true;
    } else {
      let didUpdate = false;
      Object.values(data).forEach((value) => {
        const du = searchAndPatch(value, type, id, set);
        if (du) {
          didUpdate = true;
        }
      });
      return didUpdate;
    }
  }
  return false;
}

export function useOptimisticData(id?: string) {
  const routeId = id || React.useId();
  const _loaderData = useRouteLoaderData(routeId) as LoaderData;
  const [loaderData, setLoaderData] = React.useState(_loaderData);
  React.useEffect(() => {
    setLoaderData(_loaderData);
  }, [_loaderData]);
  React.useEffect(() => {
    const listener = (e: CustomEvent) => {
      const { type, id, set } = e.detail as {
        type: string;
        id: string;
        set: Record<string, any>;
      };
      const currentTags = loaderData?.tags || [];
      const possibleTags = [`type:${type}`, `type:${type}:id:${id}`];
      if (
        type &&
        id &&
        Object.keys(set ?? {}).length &&
        possibleTags.some((tag) => currentTags.includes(tag))
      ) {
        const didUpdate = searchAndPatch(loaderData, type, id, set);
        if (didUpdate) {
          React.startTransition(() => {
            setLoaderData({ ...loaderData });
          });
        }
      }
    };
    document.addEventListener("DB:UPDATE", listener, false);
    return () => {
      document.removeEventListener("DB:UPDATE", listener, false);
    };
  }, [routeId]);

  return loaderData;
}
