"use client";

import dynamic from "next/dynamic";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
import {
  useEffect,
  useMemo,
  useState,
  useSyncExternalStore,
  type ReactNode,
} from "react";
import {
  createTanStackQueryPersister,
} from "@/lib/query-persist-storage";
import {
  registerBrowserQueryClient,
  unregisterBrowserQueryClient,
} from "@/lib/browser-query-client";

const ReactQueryDevtools =
  process.env.NODE_ENV === "development"
    ? dynamic(
        () =>
          import("@tanstack/react-query-devtools").then((mod) => ({
            default: mod.ReactQueryDevtools,
          })),
        { ssr: false },
      )
    : () => null;

const emptySubscribe = () => () => {};

function isLeadsBoardPersistQuery(queryKey: readonly unknown[]): boolean {
  return (
    queryKey[0] === "leads" &&
    queryKey[1] === "board" &&
    (queryKey[2] === "column" ||
      queryKey[2] === "search" ||
      queryKey[2] === "counts")
  );
}

export function QueryProvider({ children }: { children: ReactNode }) {
  /**
   * Defer creating the IndexedDB persister until after mount so the first client
   * render matches the server (both use QueryClientProvider). A module-level
   * `typeof window` persister made the server render QueryClientProvider while the
   * client's first paint used PersistQueryClientProvider — different trees, broken
   * React context, and "No QueryClient set" under useQuery.
   */
  const hasMounted = useSyncExternalStore(
    emptySubscribe,
    () => true,
    () => false,
  );

  const persister = useMemo((): ReturnType<
    typeof createTanStackQueryPersister
  > | null => {
    if (!hasMounted || typeof window === "undefined") return null;
    return createTanStackQueryPersister();
  }, [hasMounted]);

  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 1000 * 60 * 5, // 5 min default — prevents background revalidation storms
            gcTime: 1000 * 60 * 30,   // 30 min — was 6h, reduce memory footprint in long sessions
            refetchOnWindowFocus: false,
            refetchOnReconnect: false,
            refetchOnMount: false,    // don't re-fetch on navigation back to page
          },
        },
      }),
  );

  useEffect(() => {
    registerBrowserQueryClient(queryClient);
    return () => unregisterBrowserQueryClient();
  }, [queryClient]);

  if (!persister) {
    return (
      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
    );
  }

  return (
    <PersistQueryClientProvider
      client={queryClient}
      persistOptions={{
        persister,
        maxAge: 1000 * 60 * 60 * 24,
        dehydrateOptions: {
          /**
           * Persist successful queries to IndexedDB only (not localStorage).
           * Leads/business kanban `column` + `search` keys are separate per-stage (or search key);
           * refetch is driven by move/reorder invalidations + `refetchOnMount: false` on those queries.
           */
          shouldDehydrateQuery: (query) => {
            const rootKey = query.queryKey[0];
            const isPersistedRoot =
              rootKey === "teams" ||
              rootKey === "pipelines" ||
              rootKey === "businessDealsBoard" ||
              isLeadsBoardPersistQuery(query.queryKey);
            if (query.state.status !== "success") return false;
            if (!isPersistedRoot) return false;
            return true;
          },
        },
      }}
    >
      {children}
      {process.env.NODE_ENV === "development" ? (
        <ReactQueryDevtools buttonPosition="bottom-right" initialIsOpen={false} />
      ) : null}
    </PersistQueryClientProvider>
  );
}
