import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ReactNode,
} from "react";
import { useToast } from "../components/Toast";
import { useI18n } from "../i18n";
import { useWallet } from "../wallet/WalletProvider";
import { addFavorite, getFavoriteIds, removeFavorite } from "./api";

type FavoriteStatus = "unknown" | "favorited" | "notFavorited";

type FavoritesContextValue = {
  favoriteIds: Set<string>;
  pendingIds: Set<string>;
  statusFor: (resourceId: string) => FavoriteStatus;
  ensureFavoriteIds: (resourceIds: string[]) => Promise<void>;
  toggleFavorite: (resourceId: string) => Promise<void>;
  markFavorite: (resourceId: string, favorited: boolean) => void;
};

const FavoritesContext = createContext<FavoritesContextValue | null>(null);

export function FavoritesProvider({ children }: { children: ReactNode }) {
  const { t } = useI18n();
  const { showToast } = useToast();
  const wallet = useWallet();
  const [favoriteIds, setFavoriteIds] = useState<Set<string>>(() => new Set());
  const [knownIds, setKnownIds] = useState<Set<string>>(() => new Set());
  const [pendingIds, setPendingIds] = useState<Set<string>>(() => new Set());
  const pendingAfterLoginRef = useRef<string | null>(null);

  useEffect(() => {
    if (wallet.status === "loggedOut") {
      setFavoriteIds(new Set());
      setKnownIds(new Set());
      setPendingIds(new Set());
      pendingAfterLoginRef.current = null;
    }
  }, [wallet.status]);

  const markFavorite = useCallback((resourceId: string, favorited: boolean) => {
    setKnownIds((prev) => new Set(prev).add(resourceId));
    setFavoriteIds((prev) => {
      const next = new Set(prev);
      if (favorited) next.add(resourceId);
      else next.delete(resourceId);
      return next;
    });
  }, []);

  const ensureFavoriteIds = useCallback(
    async (resourceIds: string[]) => {
      if (!wallet.token || wallet.status !== "loggedIn") return;
      const missing = [...new Set(resourceIds)].filter(
        (id) => !knownIds.has(id),
      );
      if (missing.length === 0) return;
      const ids = await getFavoriteIds(wallet.token, missing);
      setKnownIds((prev) => {
        const next = new Set(prev);
        missing.forEach((id) => next.add(id));
        return next;
      });
      setFavoriteIds((prev) => {
        const next = new Set(prev);
        ids.forEach((id) => next.add(id));
        return next;
      });
    },
    [knownIds, wallet.status, wallet.token],
  );

  const runFavoriteMutation = useCallback(
    async (resourceId: string) => {
      if (!wallet.token) return;
      const currentlyFavorite = favoriteIds.has(resourceId);
      setPendingIds((prev) => new Set(prev).add(resourceId));
      markFavorite(resourceId, !currentlyFavorite);
      try {
        if (currentlyFavorite) await removeFavorite(wallet.token, resourceId);
        else await addFavorite(wallet.token, resourceId);
        showToast(
          currentlyFavorite ? t("favoriteRemoved") : t("favoriteAdded"),
        );
      } catch (error) {
        markFavorite(resourceId, currentlyFavorite);
        showToast(t("favoriteFailed"), "error");
        throw error;
      } finally {
        setPendingIds((prev) => {
          const next = new Set(prev);
          next.delete(resourceId);
          return next;
        });
      }
    },
    [favoriteIds, markFavorite, showToast, t, wallet.token],
  );

  const toggleFavorite = useCallback(
    async (resourceId: string) => {
      if (!wallet.token || wallet.status !== "loggedIn") {
        pendingAfterLoginRef.current = resourceId;
        wallet.openLoginModal();
        showToast(t("favoriteLoginRequired"));
        return;
      }
      await runFavoriteMutation(resourceId);
    },
    [runFavoriteMutation, showToast, t, wallet],
  );

  useEffect(() => {
    if (wallet.status !== "loggedIn" || !wallet.token) return;
    const pending = pendingAfterLoginRef.current;
    if (!pending) return;
    pendingAfterLoginRef.current = null;
    void runFavoriteMutation(pending).catch(() => undefined);
  }, [runFavoriteMutation, wallet.status, wallet.token]);

  const statusFor = useCallback(
    (resourceId: string): FavoriteStatus => {
      if (favoriteIds.has(resourceId)) return "favorited";
      if (knownIds.has(resourceId)) return "notFavorited";
      return "unknown";
    },
    [favoriteIds, knownIds],
  );

  const value = useMemo<FavoritesContextValue>(
    () => ({
      favoriteIds,
      pendingIds,
      statusFor,
      ensureFavoriteIds,
      toggleFavorite,
      markFavorite,
    }),
    [
      ensureFavoriteIds,
      favoriteIds,
      markFavorite,
      pendingIds,
      statusFor,
      toggleFavorite,
    ],
  );

  return (
    <FavoritesContext.Provider value={value}>
      {children}
    </FavoritesContext.Provider>
  );
}

export function useFavorites() {
  const ctx = useContext(FavoritesContext);
  if (!ctx)
    throw new Error("useFavorites must be used within FavoritesProvider");
  return ctx;
}
