import { useQuery, useMutation, useQueryClient, keepPreviousData } from '@tanstack/react-query';
import { useCallback } from 'react';
import { api, type QueryParams } from '@/api';

type SettingsMap = Record<string, unknown>;

// Custom hook for fetching tracker data
export function useTracker(year: number, month: number) {
  return useQuery({
    queryKey: ['tracker', year, month],
    queryFn: () => api.tracker(year, month),
    staleTime: 1000 * 60 * 5, // 5 minutes
    gcTime: 1000 * 60 * 30, // 30 minutes
  });
}

// Custom hook for fetching all bills
export function useBills() {
  return useQuery({
    queryKey: ['bills'],
    queryFn: () => api.allBills(),
    staleTime: 1000 * 60 * 5, // 5 minutes
    gcTime: 1000 * 60 * 30, // 30 minutes
  });
}

// Custom hook for fetching categories
export function useCategories() {
  return useQuery({
    queryKey: ['categories'],
    queryFn: () => api.categories(),
    staleTime: 1000 * 60 * 60, // 1 hour
    gcTime: 1000 * 60 * 60 * 2, // 2 hours
  });
}

// Lightweight overdue count for sidebar badge — polls every 5 minutes
export function useOverdueCount() {
  return useQuery({
    queryKey: ['overdue-count'],
    queryFn: () => api.overdueCount(),
    staleTime: 1000 * 60 * 2,           // 2 minutes
    refetchInterval: 1000 * 60 * 5,     // poll every 5 minutes
    refetchIntervalInBackground: false,  // only when tab is active
  });
}
// Drift / price-change report — refreshed on demand, not auto-polled
export function useDriftReport() {
  return useQuery({
    queryKey: ['drift-report'],
    queryFn: () => api.driftReport(),
    staleTime: 1000 * 60 * 10,
    refetchOnWindowFocus: false,
  });
}

// App display settings (tracker/spending layout toggles, table columns) — a
// string key/value map persisted server-side. Changes rarely; mutated via
// useSaveSettings below.
export function useSettings() {
  return useQuery({
    queryKey: ['settings'],
    queryFn: () => api.settings(),
    staleTime: 1000 * 60 * 5,
  });
}

// SimpleFIN connection status — decides whether the tracker shows the Sync
// button. Independent of tracker data, so it gets its own cache entry.
export function useSimplefinStatus() {
  return useQuery({
    queryKey: ['simplefin-status'],
    queryFn: () => api.simplefinStatus(),
    staleTime: 1000 * 60 * 5,
  });
}

// Persist a settings patch with an optimistic cache update + rollback on error.
// Replaces the hand-rolled optimistic/refetch-rollback the tracker used to do by
// hand. onSettled invalidates so the authoritative server values reconcile.
export function useSaveSettings() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: (patch: Record<string, string>) => api.saveSettings(patch),
    onMutate: async (patch) => {
      await queryClient.cancelQueries({ queryKey: ['settings'] });
      const previous = queryClient.getQueryData<SettingsMap>(['settings']);
      queryClient.setQueryData<SettingsMap>(['settings'], (old) => ({ ...(old ?? {}), ...patch }));
      return { previous };
    },
    onError: (_err, _patch, context) => {
      if (context?.previous !== undefined) {
        queryClient.setQueryData(['settings'], context.previous);
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['settings'] });
    },
  });
}

// A single invalidation for every query a bill mutation can affect. Previously a
// row action only refetch()'d the one tracker query passed down as `refresh`, so
// the sidebar overdue badge (['overdue-count'], 2-min staleTime), the drift
// report and the bills list stayed stale after paying/skipping/editing a bill —
// e.g. clearing your last overdue bill still showed "3" on the badge for minutes.
// Hand this to rows / BillModal.onSave / payment + sync handlers so the whole
// shell updates live. Returns a stable callback safe to use as an effect dep.
export function useInvalidateTrackerData() {
  const queryClient = useQueryClient();
  return useCallback(() => {
    for (const key of [['tracker'], ['overdue-count'], ['drift-report'], ['bills']]) {
      queryClient.invalidateQueries({ queryKey: key });
    }
  }, [queryClient]);
}

// Prefetch a tracker month into the cache (e.g. on month-nav hover) so switching
// to it is instant. No-op if it's already cached and fresh.
export function usePrefetchTracker() {
  const queryClient = useQueryClient();
  return useCallback((year: number, month: number) => {
    queryClient.prefetchQuery({
      queryKey: ['tracker', year, month],
      queryFn: () => api.tracker(year, month),
      staleTime: 1000 * 60 * 5,
    });
  }, [queryClient]);
}

// ── Page data (migrated off manual useEffect + load()) ───────────────────────
// The queryKey encodes the params, so React Query handles caching, request
// dedup, cancellation, and out-of-order responses — no manual sequence guards.
// keepPreviousData keeps the last result visible while a new month/filter loads.

export function useAnalyticsSummary(params?: QueryParams) {
  return useQuery({
    queryKey: ['analytics-summary', params],
    queryFn: () => api.analyticsSummary(params),
    staleTime: 1000 * 60 * 2,
    placeholderData: keepPreviousData,
  });
}

export function useBillTemplates() {
  return useQuery({
    queryKey: ['bill-templates'],
    queryFn: () => api.billTemplates(),
    staleTime: 1000 * 60 * 5,
  });
}

export function useDeletedBills() {
  return useQuery({
    queryKey: ['deleted-bills'],
    queryFn: () => api.deletedBills().catch(() => []), // non-critical: never block the page
    staleTime: 1000 * 60 * 5,
  });
}

export function useSummary(year: number, month: number) {
  return useQuery({
    queryKey: ['summary', year, month],
    queryFn: () => api.summary(year, month),
    staleTime: 1000 * 60 * 2,
    placeholderData: keepPreviousData,
    // Editable form fields (starting amounts, income) are seeded from this
    // result via an effect; don't let a focus refetch reset a mid-edit.
    refetchOnWindowFocus: false,
  });
}

interface BankLedgerArgs {
  accountId: string;
  flow: string;
  page: number;
  query: string;
  sortBy: string;
  sortDir: string;
  pageSize: number;
}

export function useBankLedger({ accountId, flow, page, query, sortBy, sortDir, pageSize }: BankLedgerArgs) {
  return useQuery({
    queryKey: ['bank-ledger', accountId, flow, page, query, sortBy, sortDir],
    queryFn: () => api.bankTransactionsLedger({
      limit: pageSize,
      offset: (page - 1) * pageSize,
      q: query,
      account_id: accountId === 'all' ? undefined : accountId,
      flow,
      sort_by: sortBy,
      sort_dir: sortDir,
    }),
    staleTime: 1000 * 60,
    placeholderData: keepPreviousData,
  });
}

export function useSpendingSummary(year: number, month: number) {
  return useQuery({
    queryKey: ['spending-summary', year, month],
    queryFn: () => api.spendingSummary({ year, month }),
    staleTime: 1000 * 60 * 2,
    placeholderData: keepPreviousData,
  });
}

interface SpendingTransactionsArgs {
  year: number;
  month: number;
  activeCat?: number | string | null;
  page: number;
}

export function useSpendingTransactions({ year, month, activeCat, page }: SpendingTransactionsArgs) {
  return useQuery({
    queryKey: ['spending-transactions', year, month, activeCat ?? 'all', page],
    queryFn: () => {
      const params: QueryParams = { year, month, page, limit: 50 };
      if (activeCat === null) params.category_id = 'null';
      else if (activeCat !== undefined) params.category_id = activeCat;
      return api.spendingTransactions(params);
    },
    staleTime: 1000 * 60 * 2,
    placeholderData: keepPreviousData,
  });
}

export function useSpendingCategories() {
  return useQuery({
    queryKey: ['spending-categories'],
    queryFn: async () => {
      const cats = await api.categories();
      return cats.filter(c => !c.deleted_at && c.spending_enabled);
    },
    staleTime: 1000 * 60 * 5,
  });
}

export function useCategoryGroups() {
  return useQuery({
    queryKey: ['category-groups'],
    queryFn: () => api.categoryGroups().then(g => g || []).catch(() => []),
    staleTime: 1000 * 60 * 5,
  });
}

export function useSnowball() {
  return useQuery({ queryKey: ['snowball'], queryFn: () => api.snowball(), staleTime: 1000 * 60 * 2 });
}

export function useSnowballSettings() {
  return useQuery({ queryKey: ['snowball-settings'], queryFn: () => api.snowballSettings(), staleTime: 1000 * 60 * 2 });
}

export function useSnowballActivePlan() {
  return useQuery({
    queryKey: ['snowball-active-plan'],
    queryFn: () => api.snowballActivePlan().catch(() => null),
    staleTime: 1000 * 60 * 2,
  });
}

export function useSnowballPlans() {
  return useQuery({
    queryKey: ['snowball-plans'],
    queryFn: () => api.snowballPlans().then(d => d?.plans ?? []).catch(() => []),
    staleTime: 1000 * 60 * 2,
  });
}

export function useSubscriptions() {
  return useQuery({
    queryKey: ['subscriptions'],
    queryFn: () => api.subscriptions(),
    staleTime: 1000 * 60 * 5,
  });
}

export function useSubscriptionRecommendations() {
  return useQuery({
    queryKey: ['subscription-recommendations'],
    queryFn: () => api.subscriptionRecommendations().then(r => r.recommendations || []),
    staleTime: 1000 * 60 * 5,
  });
}
