/**
 * Global QueryClient factory for Multideal.
 *
 * - Browser: singleton via `getBrowserQueryClient()` — shared across all HydratedIsland mounts.
 * - Server (Cloudflare Worker): fresh instance per request via `createServerQueryClient()`.
 *   Never reuse across requests.
 */

import { QueryClient, keepPreviousData } from '@tanstack/react-query';
import type { Query } from '@tanstack/react-query';
import { clearPersistedCache, installPersistence } from '@platform-modules/query-react/persist';
import { applyMhCookie } from '@/lib/mh-cookie';

declare const __BUILD_ID__: string;

/** Create a fresh browser QueryClient with production defaults. */
export function createBrowserQueryClient(): QueryClient {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60_000,
        gcTime: 30 * 60_000,
        refetchOnWindowFocus: false,
        refetchOnReconnect: true,
        refetchIntervalInBackground: false,
        retry: 1,
        placeholderData: keepPreviousData,
      },
      mutations: { retry: 0 },
    },
  });
}

let _client: QueryClient | undefined;
let _uninstallPersistence: (() => void) | undefined;
let _visibilityListenerArmed = false;

/**
 * PWA resume-refresh threshold: only invalidate cached queries when the tab
 * returns to foreground after being hidden longer than this. Keeps brief
 * alt-tabs cheap while still refreshing stale data on real PWA resume from
 * background (mobile app switcher, device wake, etc).
 */
const VISIBILITY_REFRESH_THRESHOLD_MS = 120_000;
const BUILD_ID = typeof __BUILD_ID__ !== 'undefined' ? __BUILD_ID__ : 'dev';
const SENSITIVE_QUERY_ROOTS = new Set([
  'admin',
  'admin-affiliate',
  'admin-affiliate-commission-history',
  'admin-affiliate-payouts',
  'admin-llm-queues',
  'admin-platform-settings',
  'admin-settlements',
  'admin-share',
  'admin-support',
  'admin-tags',
  'admin-vat-rates',
  'affiliate-activity',
  'affiliate-connect-status',
  'affiliate-settings',
  'affiliate-stats',
  'affiliate-wallet',
  'cart',
  'case',
  'cases',
  'chat',
  'favorites',
  'fraud-analytics-events-over-time',
  'fraud-analytics-fp-rate',
  'fraud-analytics-money-protected',
  'fraud-analytics-top-adapters',
  'fraud-evidence',
  'fraud-events',
  'fraud-stats',
  'gravatar-check',
  'mh-page',
  'notifications',
  'notif-prefs',
  'profile',
  'purchase-context',
  'purchase-messages',
  'purchases',
  'recently-viewed',
  'referrals',
  'session',
  'support-ticket',
  'support-tickets',
  'user',
  'vendor-analytics',
  'vendor-customers',
  'vendor-dashboard',
  'vendor-deals',
  'vendor-draft-count',
  'vendor-group-deal-dashboard',
  'vendor-hours',
  'vendor-notif-prefs',
  'vendor-profile',
  'vendor-promo-code',
  'vendor-promo-codes',
  'vendor-purchase-messages',
  'wishlist',
]);

function shouldDehydrateQuery(query: Query): boolean {
  if (query.state.status !== 'success') return false;
  if (query.meta?.persist === false) return false;

  const [root, scope] = query.queryKey;
  if (typeof root !== 'string') return true;
  if (SENSITIVE_QUERY_ROOTS.has(root)) return false;
  if (root === 'vendor' && scope === 'drafts') return false;
  return !root.startsWith('admin-');
}

function readPersistenceUserId(): string | null {
  applyMhCookie();
  const identity = window.__MH__?.i?.trim();
  return identity && identity.length > 0 ? identity : null;
}

/**
 * Arms a single document-level `visibilitychange` listener that invalidates
 * the browser QueryClient when the tab returns to foreground after being
 * hidden for more than `VISIBILITY_REFRESH_THRESHOLD_MS`.
 *
 * Idempotent — the module-level `_visibilityListenerArmed` flag guarantees
 * the listener is registered exactly once for the page lifetime, regardless
 * of how many HydratedIsland mounts call `getBrowserQueryClient()`. Keeps
 * the global TanStack `refetchOnWindowFocus: false` setting intact: this is
 * a stricter, debounced PWA-resume signal, not a per-focus refetch.
 */
function armVisibilityRefresh(client: QueryClient): void {
  if (_visibilityListenerArmed) return;
  if (typeof document === 'undefined') return;
  _visibilityListenerArmed = true;

  let hiddenAt: number | null = null;

  document.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') {
      hiddenAt = Date.now();
      return;
    }
    if (document.visibilityState === 'visible' && hiddenAt !== null) {
      const hiddenFor = Date.now() - hiddenAt;
      hiddenAt = null;
      if (hiddenFor > VISIBILITY_REFRESH_THRESHOLD_MS) {
        void client.invalidateQueries();
      }
    }
  });
}

/**
 * Returns the global browser-side QueryClient singleton.
 * Throws if called on the server — use `createServerQueryClient()` there.
 */
export function getBrowserQueryClient(): QueryClient {
  if (typeof window === 'undefined') {
    throw new Error('getBrowserQueryClient: server context — use createServerQueryClient');
  }
  if (!_client) {
    _client = createBrowserQueryClient();
  }
  if (!_uninstallPersistence) {
    _uninstallPersistence = installPersistence(_client, {
      userId: readPersistenceUserId(),
      buster: BUILD_ID,
      shouldDehydrateQuery,
    });
  }
  armVisibilityRefresh(_client);
  return _client;
}

export function clearBrowserQueryClientForLogout(): void {
  _uninstallPersistence?.();
  _uninstallPersistence = undefined;
  clearPersistedCache();
  _client?.clear();
}

/**
 * Create a fresh per-request server QueryClient.
 * Never share across requests — call once per Astro frontmatter execution.
 */
export function createServerQueryClient(): QueryClient {
  return new QueryClient({
    defaultOptions: { queries: { staleTime: Infinity, retry: 0 } },
  });
}
