import { infiniteQueryOptions, queryOptions } from '@tanstack/react-query'
import {
  fetchNotifications,
  fetchNotificationsV2,
  fetchNotificationsV2Mastodon,
  getFilteredNotificationRequests,
  getNewestNotificationId,
  getNotificationPolicy,
  probeNotificationsV2,
  pushNotificationSupported,
  pushState,
} from 'src/lib/api'
import type { PaginatedStatus } from 'src/lib/api-types'
import { activeProtocol } from 'src/lib/protocol'
import { AccountStore } from 'src/state/accountStore'
import { queryKeys } from './keys'

type Page = {
  data: any
  nextPage?: string
  prevPage?: string
}

// Phase 4 §5.1: route the v2 grouped fetch by the active account type. Both
// fetchers return the same `{ data: { notification_groups, notifications,
// accounts, statuses }, nextPage, prevPage }` envelope so the screen renderer
// stays type-agnostic. The Mastodon variant uses documented Mastodon
// parameters (`exclude_types[]`, `expand_accounts=partial_avatars`,
// `grouped_types[]` covering the server-side default grouping set) and skips
// the Pixelfed-extended `_pe=1` opt-in (handled centrally by ApiContext).
function fetchGroupedByActiveAccountType(args: {
  tab: string
  pageParam?: string | false
}): Promise<Page> {
  const type = AccountStore.activeAccountOrThrow().type
  if (type === 'mastodon') return fetchNotificationsV2Mastodon(args)
  return fetchNotificationsV2(args)
}

// Phase 4 §5.1: Mastodon ≥ 4.4 guarantees grouped v2 is reachable, so the
// probe short-circuits to true on Mastodon contexts. The Pixelfed probe
// stays — Pixelfed's v2 grouping is gated by an admin feature flag and not
// every instance has it on yet.
async function probeGroupedByActiveAccountType(): Promise<boolean> {
  const type = AccountStore.activeAccountOrThrow().type
  if (type === 'mastodon') return true
  return await probeNotificationsV2()
}

export const notificationQueries = {
  list: (tab: string) =>
    infiniteQueryOptions({
      queryKey: queryKeys.notifications.byTab(tab),
      queryFn: ({ pageParam }) => fetchNotifications({ tab, pageParam }),
      initialPageParam: undefined as string | undefined,
      staleTime: 60 * 1000,
      gcTime: 30 * 60 * 1000,
      getNextPageParam: (lastPage: PaginatedStatus) => lastPage.nextPage ?? undefined,
      getPreviousPageParam: (lastPage: PaginatedStatus) => lastPage.prevPage ?? undefined,
    }),

  grouped: (tab: string) =>
    infiniteQueryOptions({
      queryKey: queryKeys.notifications.grouped(tab),
      queryFn: ({ pageParam }): Promise<Page> =>
        fetchGroupedByActiveAccountType({ tab, pageParam }),
      initialPageParam: undefined as string | undefined,
      staleTime: 60 * 1000,
      gcTime: 30 * 60 * 1000,
      getNextPageParam: (lastPage: Page) => lastPage.nextPage ?? undefined,
      getPreviousPageParam: (lastPage: Page) => lastPage.prevPage ?? undefined,
    }),

  v2Probe: () =>
    queryOptions({
      queryKey: queryKeys.notifications.v2Probe(),
      queryFn: probeGroupedByActiveAccountType,
      staleTime: Number.POSITIVE_INFINITY,
      gcTime: Number.POSITIVE_INFINITY,
    }),

  policy: () =>
    queryOptions({
      queryKey: queryKeys.notifications.policy(),
      queryFn: getNotificationPolicy,
      staleTime: 5 * 60 * 1000,
      gcTime: 30 * 60 * 1000,
    }),

  requests: () =>
    infiniteQueryOptions({
      queryKey: queryKeys.notifications.requests(),
      queryFn: ({ pageParam }): Promise<Page> =>
        getFilteredNotificationRequests({ pageParam }),
      initialPageParam: undefined as string | undefined,
      staleTime: 60 * 1000,
      gcTime: 30 * 60 * 1000,
      getNextPageParam: (lastPage: Page) => lastPage.nextPage ?? undefined,
      getPreviousPageParam: (lastPage: Page) => lastPage.prevPage ?? undefined,
    }),

  pushState: () =>
    queryOptions({
      queryKey: queryKeys.notifications.pushState(),
      queryFn: pushState,
      staleTime: 5 * 60 * 1000,
      gcTime: 30 * 60 * 1000,
    }),

  pushCheck: () =>
    queryOptions({
      queryKey: queryKeys.notifications.pushCheck(),
      queryFn: pushNotificationSupported,
      staleTime: 60 * 60 * 1000,
      gcTime: Number.POSITIVE_INFINITY,
    }),

  // Polled every minute while foregrounded; the count drives both the
  // tab-bar bell badge and the OS app-icon badge. Counted server-side by
  // fetching notifications newer than the persisted marker and measuring
  // the list length — capped at 40 so the badge work stays cheap. On
  // Mastodon (Phase 3.3) this delegates to `/api/v1/notifications/unread_count`
  // for a single-call canonical answer; the React Query shape is unchanged.
  unreadCount: () =>
    queryOptions({
      queryKey: queryKeys.notifications.unreadCount(),
      queryFn: () => activeProtocol().getNotificationUnreadCount(),
      refetchInterval: 60 * 1000,
      refetchOnWindowFocus: true,
      staleTime: 30 * 1000,
      gcTime: 30 * 60 * 1000,
    }),

  // Holistic Pass 4 Phase 3.2 — newest unfiltered notification id, used on
  // tab-focus to advance the server-side marker without conflating with the
  // currently-active filter (the previous `newestNotificationId` derivation
  // read from the filtered query, which over- or under-counted the badge
  // depending on which tab the user left the screen on). Independent of
  // which tab is active so the marker write target is always canonical.
  newestId: () =>
    queryOptions({
      queryKey: [...queryKeys.notifications.allPrefix(), 'newestId'] as const,
      queryFn: getNewestNotificationId,
      staleTime: 30 * 1000,
      gcTime: 30 * 60 * 1000,
    }),
}
