import { createContext, useCallback, useContext, useEffect, useState } from "react";
import { UNSAFE_NavigationContext, useBeforeUnload, useBlocker } from "react-router-dom";
import { UnsavedChangesDialog } from "./components";

type ContextValue = {
  requestConfirm: () => Promise<boolean>;
};

export const UnsavedChangesContext = createContext<ContextValue | null>(null);

export const useUnsavedChangesContext = () => {
  const context = useContext(UnsavedChangesContext);
  if (context === null) {
    throw new Error("useUnsavedChangesContext must be used within an UnsavedChangesProvider");
  }
  return context;
};

export function UnsavedChangesProvider({
  isDirty,
  children,
}: {
  isDirty: boolean;
  children: React.ReactNode;
}) {
  const [resolver, setResolver] = useState<(ok: boolean) => void>();
  const [open, setOpen] = useState(false);

  // requestConfirm() devuelve una promesa que resuelve true/false
  // según lo que haya pulsado el usuario
  const requestConfirm = useCallback(() => {
    return new Promise<boolean>((resolve) => {
      setResolver(() => resolve);
      setOpen(true);
    });
  }, []);

  const handleConfirm = (ok: boolean) => {
    resolver?.(ok);
    setResolver(undefined);
    setOpen(false);
  };

  // 🔹 bloquea F5/cierre de pestaña
  useBeforeUnload(
    (event) => {
      if (isDirty) {
        event.preventDefault();
      }
    },
    { capture: true }
  );

  // 🔹 bloquea navegación interna (react-router)
  const navigator = useContext(UNSAFE_NavigationContext).navigator as any;

  useEffect(() => {
    if (!isDirty) return;

    const push = navigator.push;
    navigator.push = async (...args: any[]) => {
      const ok = await requestConfirm();
      if (ok) push.apply(navigator, args);
    };

    return () => {
      navigator.push = push;
    };
  }, [isDirty, requestConfirm, navigator]);

  // 🔹 Bloquea navegación entre rutas
  const blocker = useBlocker(() => isDirty);

  useEffect(() => {
    if (blocker.state === "blocked") {
      (async () => {
        const ok = await requestConfirm();
        if (ok) {
          blocker.proceed();
        } else {
          blocker.reset();
        }
      })();
    }
  }, [blocker, requestConfirm]);

  return (
    <UnsavedChangesContext.Provider value={{ requestConfirm }}>
      {children}
      <UnsavedChangesDialog open={open} onConfirm={handleConfirm} />
    </UnsavedChangesContext.Provider>
  );
}
