import { useIsFocused } from '@react-navigation/native'
import { FlashList, type FlashListRef } from '@shopify/flash-list'
import { useInfiniteQuery, useQuery, useQueryClient } from '@tanstack/react-query'
import { Image } from 'expo-image'
import type { ErrorBoundaryProps } from 'expo-router'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { RefreshControl } from 'react-native'
import { EmptyState } from 'src/components/common/EmptyState'
import { ErrorState } from 'src/components/common/ErrorState'
import { ListFooter } from 'src/components/common/ListFooter'
import { LoadingView } from 'src/components/common/LoadingView'
import {
  ScreenScrollContainer,
  useAccentRefreshControlProps,
} from 'src/components/layout'
import { useMenuScroll, useScreenMenu, useTabPressScrollToTop } from 'src/components/menu'
import NotificationGroup from 'src/components/notifications/NotificationGroup'
import RenderNotificationItem from 'src/components/notifications/RenderNotificationItem'
import type { Account, Status } from 'src/lib/api-types'
import { haptic } from 'src/lib/haptics'
import { advanceMarkerOnFocus } from 'src/lib/notifications/marker'
import { queryKeys } from 'src/lib/queries/keys'
import { notificationQueries } from 'src/lib/queries/notifications'
import { emit } from 'src/lib/tapestry/emit'
import { Text, View } from 'tamagui'

const NOTIFICATION_FILTER_OPTIONS = [
  { value: 'all', label: 'All', icon: 'activity' as const },
  { value: 'mentions', label: 'Mentions', icon: 'at-sign' as const },
  { value: 'likes', label: 'Likes', icon: 'heart' as const },
  { value: 'follows', label: 'Follows', icon: 'user-plus' as const },
  { value: 'reblogs', label: 'Reblogs', icon: 'repeat' as const },
]

export function ErrorBoundary({ error, retry }: ErrorBoundaryProps) {
  return <ErrorState message={error.message} onRetry={retry} />
}

type GroupedRow = {
  kind: 'group'
  group: any
  accounts: Account[]
  statuses: Status[]
}

export default function NotificationsScreen() {
  const queryClient = useQueryClient()
  const isFocused = useIsFocused()

  const [activeTab, setActiveTab] = useState('all')
  // `/api/v2/notifications` is the default. The probe decides whether we can
  // use grouped rendering at all; on servers without v2 support we fall back
  // to the v1 flat list. While the probe is in flight we prefer *not* to
  // issue a v1 fetch so we don't double-request on v2-capable instances.
  const { data: v2Supported, isFetching: v2Probing } = useQuery(
    notificationQueries.v2Probe()
  )
  const useGrouped = v2Supported === true
  const useFlat = v2Supported === false

  const v1 = useInfiniteQuery({
    ...notificationQueries.list(activeTab),
    subscribed: isFocused && useFlat,
    enabled: useFlat,
  })

  const v2 = useInfiniteQuery({
    ...notificationQueries.grouped(activeTab),
    subscribed: isFocused && useGrouped,
    enabled: useGrouped,
  })

  const active = useGrouped ? v2 : v1

  const handleTabChange = (value: any) => {
    haptic.selection()
    queryClient.invalidateQueries({
      queryKey: useGrouped
        ? notificationQueries.grouped(value).queryKey
        : notificationQueries.list(value).queryKey,
    })
    setActiveTab(value)
  }

  useScreenMenu({
    intents: [
      {
        kind: 'filter',
        accessibilityLabel: 'Notification filter',
        value: activeTab,
        options: NOTIFICATION_FILTER_OPTIONS,
        onChange: handleTabChange,
        display: 'icons',
      },
      // `/chats` accepts Mastodon-active sessions: chat-shape.ts wires both
      // wire shapes through, useRequirePixelfed() was dropped at the screen
      // top, and visibility is identical on both account types.
      {
        kind: 'nav',
        id: 'dm',
        icon: 'mail',
        label: 'Direct messages',
        to: '/chats',
        accessibilityLabel: 'Direct messages',
      },
    ],
  })

  const handleInfiniteScroll = () => {
    if (active.isFetchingNextPage) return
    if (active.hasNextPage) active.fetchNextPage()
  }

  const v1FlatData = useMemo(() => {
    if (useGrouped) return []
    return v1.data?.pages.flatMap((page) => page.data) ?? []
  }, [useGrouped, v1.data?.pages])

  const v2FlatData = useMemo<GroupedRow[]>(() => {
    if (!useGrouped) return []
    const rows: GroupedRow[] = []
    for (const page of v2.data?.pages ?? []) {
      const payload: any = page?.data
      const groups: any[] = payload?.notification_groups ?? []
      const accounts: Account[] = payload?.accounts ?? []
      const statuses: Status[] = payload?.statuses ?? []
      for (const group of groups) {
        rows.push({ kind: 'group', group, accounts, statuses })
      }
    }
    return rows
  }, [useGrouped, v2.data?.pages])

  const onScroll = useMenuScroll(true)
  const refreshProps = useAccentRefreshControlProps()

  // Single ref shared between the v2-grouped and v1-flat FlashLists; only
  // one renders at a time so the ref always points at the active list.
  const listRef = useRef<FlashListRef<any> | null>(null)
  const onTabRefetch = useCallback(() => {
    active.refetch()
    queryClient.invalidateQueries({
      queryKey: queryKeys.notifications.unreadCount(),
    })
    // Phase 3.2 — keep the newest-id query fresh so the focus-marker advance
    // fires against the latest server state on re-tap.
    queryClient.invalidateQueries({
      queryKey: notificationQueries.newestId().queryKey,
    })
  }, [active, queryClient])
  useTabPressScrollToTop('notifications', listRef, onTabRefetch)

  useEffect(() => {
    const pages = useGrouped ? v2.data?.pages : v1.data?.pages
    if (!pages?.length) return
    const lastPage: any = pages[pages.length - 1]
    const statuses: Status[] = useGrouped
      ? (lastPage?.data?.statuses ?? [])
      : (lastPage?.data ?? []).map((n: any) => n.status).filter(Boolean)
    const urls = statuses
      .map((s: any) => s?.media_attachments?.[0]?.url)
      .filter((u: unknown): u is string => typeof u === 'string')
    if (urls.length) Image.prefetch(urls, 'memory-disk')
  }, [useGrouped, v1.data?.pages, v2.data?.pages])

  // Holistic Pass 4 Phase 3.2: the marker write target is the *unfiltered*
  // newest id, not the currently-filtered list's first id. The previous
  // shape (read from `v1.data?.pages[0].data[0].id`) over-counted when the
  // user left on a sub-filter (e.g. Mentions) — the marker advanced past
  // newer non-mention notifications the user had never seen. The dedicated
  // tiny query below stays warm regardless of which tab is active.
  const { data: newestId } = useQuery({
    ...notificationQueries.newestId(),
    enabled: isFocused,
  })

  // Mark-as-read: when the user focuses the Notifications tab and we know
  // the unfiltered newest id, advance both the local mirror (Phase 3.1) and
  // the server marker, then clear the unread-count badge. Re-runs if a fresh
  // page arrives while the tab stays focused.
  useEffect(() => {
    if (!isFocused || !newestId) return
    let cancelled = false
    ;(async () => {
      try {
        await advanceMarkerOnFocus(newestId)
        if (cancelled) return
        emit('N')
        queryClient.setQueryData(queryKeys.notifications.unreadCount(), 0)
        queryClient.invalidateQueries({
          queryKey: queryKeys.notifications.unreadCount(),
        })
      } catch {
        // Transient network error — badge will re-sync on the next 60s poll.
      }
    })()
    return () => {
      cancelled = true
    }
  }, [isFocused, newestId, queryClient])

  if (
    v2Probing ||
    (active.isFetching && !active.isFetchingNextPage && !active.isRefetching)
  )
    return <LoadingView />
  if (active.error) return <ErrorState onRetry={active.refetch} />

  return (
    <ScreenScrollContainer variant="flashlist">
      {(scrollProps) =>
        useGrouped ? (
          <FlashList
            ref={listRef}
            data={v2FlatData}
            renderItem={({ item }) => (
              <NotificationGroup
                group={item.group}
                accounts={item.accounts}
                statuses={item.statuses}
              />
            )}
            keyExtractor={(item, index) => `${item.group.group_key}_${index.toString()}`}
            onEndReached={handleInfiniteScroll}
            refreshControl={
              <RefreshControl
                refreshing={active.isRefetching}
                onRefresh={() =>
                  queryClient
                    .invalidateQueries({
                      queryKey: notificationQueries.grouped(activeTab).queryKey,
                    })
                    .then(() => haptic.light())
                }
                progressViewOffset={scrollProps.topInset}
                {...refreshProps}
              />
            }
            onEndReachedThreshold={0.5}
            contentInsetAdjustmentBehavior={scrollProps.contentInsetAdjustmentBehavior}
            contentInset={scrollProps.contentInset}
            scrollIndicatorInsets={scrollProps.scrollIndicatorInsets}
            contentContainerStyle={{
              ...scrollProps.contentContainerStyle,
              flexGrow: 1,
            }}
            onScroll={onScroll}
            ListEmptyComponent={
              <EmptyState
                icon="bell"
                title="No notifications yet"
                subtitle="Likes, follows, mentions and event activity will show up here."
              />
            }
            ListFooterComponent={
              <View py="$6">
                <ListFooter loading={active.isFetchingNextPage} />
                {v2FlatData.length > 0 &&
                !active.hasNextPage &&
                !active.isFetchingNextPage ? (
                  <Text ta="center" color="$color">
                    No more notifications for now!
                  </Text>
                ) : null}
              </View>
            }
          />
        ) : (
          <FlashList
            ref={listRef}
            data={v1FlatData}
            renderItem={({ item }) => <RenderNotificationItem item={item} />}
            keyExtractor={(item, index) => `${item.id}_${index.toString()}`}
            onEndReached={handleInfiniteScroll}
            refreshControl={
              <RefreshControl
                refreshing={active.isRefetching}
                onRefresh={() =>
                  queryClient
                    .invalidateQueries({
                      queryKey: notificationQueries.list(activeTab).queryKey,
                    })
                    .then(() => haptic.light())
                }
                progressViewOffset={scrollProps.topInset}
                {...refreshProps}
              />
            }
            onEndReachedThreshold={0.5}
            contentInsetAdjustmentBehavior={scrollProps.contentInsetAdjustmentBehavior}
            contentInset={scrollProps.contentInset}
            scrollIndicatorInsets={scrollProps.scrollIndicatorInsets}
            contentContainerStyle={{
              ...scrollProps.contentContainerStyle,
              flexGrow: 1,
            }}
            onScroll={onScroll}
            ListEmptyComponent={
              <EmptyState
                icon="bell"
                title="No notifications yet"
                subtitle="Likes, follows, mentions and event activity will show up here."
              />
            }
            ListFooterComponent={
              <View py="$6">
                <ListFooter loading={active.isFetchingNextPage} />
                {v1FlatData.length > 0 &&
                !active.hasNextPage &&
                !active.isFetchingNextPage ? (
                  <Text ta="center" color="$color">
                    No more notifications for now!
                  </Text>
                ) : null}
              </View>
            }
          />
        )
      }
    </ScreenScrollContainer>
  )
}
