import {
  createContext,
  useCallback,
  useMemo,
  useState,
  type ComponentType,
  type ReactNode,
} from "react";
import { idAssert } from "../../util/ts.js";

export interface PopupManagerContextData {
  openPopup(popup: ComponentType<{ id: string; onClose: () => void }>): string;
  openPopup<T>(
    popup: ComponentType<{ id: string; onClose: () => void } & T>,
    props: T
  ): string;

  closePopup(id: string): void;
}

export const PopupManagerContext =
  createContext<PopupManagerContextData | null>(null);
PopupManagerContext.displayName = "PopupManagerContext";

type Popup = {
  id: string;
  Component: ComponentType<{ id: string; onClose: () => void }>;
  props: { id: string; onClose: () => void };
};

export function PopupManager({ children }: { children: ReactNode }) {
  const [popups, setPopups] = useState<Popup[]>([]);

  const openPopup = useCallback<PopupManagerContextData["openPopup"]>(
    (
      Component: ComponentType<{ id: string; onClose: () => void }>,
      props = {}
    ) => {
      const id = crypto.randomUUID();
      Object.assign(props, {
        id,
        onClose: () => setPopups((prev) => prev.filter((x) => x.id !== id)),
      });
      idAssert<{ id: string; onClose: () => void }>(props);
      setPopups((prev) => [...prev, { id, Component, props }]);
      return id;
    },
    []
  );
  const closePopup = useCallback((id: string) => {
    setPopups((prev) => prev.filter((x) => x.id !== id));
  }, []);
  const ctx = useMemo<PopupManagerContextData>(
    () => ({ openPopup, closePopup }),
    []
  );

  return (
    <PopupManagerContext value={ctx}>
      {children}
      {popups.map((popup) => (
        <popup.Component key={popup.id} {...popup.props} />
      ))}
    </PopupManagerContext>
  );
}
