import { Feather } from '@expo/vector-icons'
import { FlashList } from '@shopify/flash-list'
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import { router, useLocalSearchParams } from 'expo-router'
import { useMemo, useState } from 'react'
import { Pressable } from 'react-native'
import EmojiText from 'src/components/common/EmojiText'
import { ErrorState } from 'src/components/common/ErrorState'
import { ListFooter as ListFooterIndicator } from 'src/components/common/ListFooter'
import { LoadingView } from 'src/components/common/LoadingView'
import { UserRow } from 'src/components/common/UserRow'
import { ScreenScrollContainer } from 'src/components/layout'
import { useMenuScroll, useScreenMenu, useSearchQuery } from 'src/components/menu'
import { BodyText, FootnoteText } from 'src/components/ui'
import { useAddListMembersMutation } from 'src/hooks/mutations/useAddListMembersMutation'
import type { Account } from 'src/lib/api-types'
import { haptic } from 'src/lib/haptics'
import { accountQueries } from 'src/lib/queries/accounts'
import { listsQueries } from 'src/lib/queries/lists'
import { useActiveProfile, useAuth } from 'src/state/AuthProvider'
import { Separator, useTheme, useToastController, View, XStack, YStack } from 'tamagui'

// Surface C — "Pick from your follows" add-members picker. Forked from
// `AccountListScreen` (per proposal §9.5: parameterizing it would leak
// selection state into existing read-only consumers — fork is cheaper).
//
// Selection lives in local state; one tap toggles. The "Add (N)" commit fires
// a single `useAddListMembersMutation` call with the full selection, so the
// 350ms-per-list debouncer FediverseLists needed isn't needed here.
export default function AddListMembersScreen() {
  const theme = useTheme()
  const toast = useToastController()
  const { id: listId } = useLocalSearchParams<{ id: string }>()
  const { user } = useAuth()
  const isMastodon = user?.type === 'mastodon'
  const { summary } = useActiveProfile()
  const selfId = summary?.userId ?? ''

  const [query, setQuery] = useSearchQuery()
  const [selected, setSelected] = useState<Map<string, Account>>(new Map())
  const addMembers = useAddListMembersMutation()

  // Resolve the canonical list row from the catalogue so we can render the
  // screen title and detect when the list disappeared mid-flow.
  const { data: lists } = useQuery({
    ...listsQueries.lists(),
    enabled: isMastodon && !!listId,
  })
  const list = useMemo(() => lists?.find((l) => l.id === listId) ?? null, [lists, listId])

  // Lists in `forAccount` are unpaginated. Read the per-list members cache
  // (already populated when the user landed on Surface B) so the picker
  // greys out / hides accounts who are already members. The cache only
  // covers the pages the user has scrolled through; treating it as a
  // best-effort shortlist is safe — adding an already-member is a no-op
  // server-side (Mastodon returns 200 either way), so a false-negative
  // means at worst one extra redundant id in the POST.
  const { data: members } = useInfiniteQuery({
    ...listsQueries.accounts(listId as string),
    enabled: isMastodon && !!listId,
  })
  const existingMemberIds = useMemo<Set<string>>(
    () => new Set((members?.pages ?? []).flatMap((p) => p.data.map((a) => a.id))),
    [members]
  )

  const {
    data: following,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    isFetching,
    error,
    refetch,
  } = useInfiniteQuery({
    ...accountQueries.following(selfId),
    enabled: !!selfId && isMastodon,
  })

  const flattened = useMemo<Account[]>(() => {
    const all = (following?.pages ?? []).flatMap((p: any) => p.data ?? []) as Account[]
    const q = query.trim().toLowerCase()
    const filtered = q
      ? all.filter((a) => {
          const acct = a.acct?.toLowerCase() ?? ''
          const name = a.display_name?.toLowerCase() ?? ''
          return acct.includes(q) || name.includes(q)
        })
      : all
    // Hide accounts already in the list — the picker is for adding, not for
    // toggling existing membership (Surface B's swipe-to-remove handles that).
    return filtered.filter((a) => !existingMemberIds.has(a.id))
  }, [following, query, existingMemberIds])

  const toggle = (account: Account) => {
    haptic.selection()
    setSelected((prev) => {
      const next = new Map(prev)
      if (next.has(account.id)) next.delete(account.id)
      else next.set(account.id, account)
      return next
    })
  }

  const onCommit = () => {
    if (selected.size === 0 || addMembers.isPending || !listId) return
    const accounts = Array.from(selected.values())
    addMembers.mutate(
      {
        listId,
        accountIds: accounts.map((a) => a.id),
        accounts,
      },
      {
        onSuccess: () => router.back(),
        onError: () =>
          toast.show('Could not add to list', { preset: 'error', duration: 2500 }),
      }
    )
  }

  // The native `useScreenMenu` title is a string for system-nav reasons; we
  // can't render custom-emoji shortcodes there. The sticky-header below uses
  // EmojiText so the in-app title gets the shortcodes rendered visually.
  useScreenMenu({
    title: list ? `Add to “${list.title}”` : 'Add members',
    intents: [
      { kind: 'back' },
      {
        kind: 'search',
        placeholder: 'Filter follows',
        value: query,
        onChangeText: setQuery,
      },
      {
        kind: 'commit',
        id: 'add-members',
        icon: 'check',
        label: selected.size > 0 ? `Add (${selected.size})` : 'Add',
        onPress: onCommit,
        state: addMembers.isPending ? 'busy' : selected.size > 0 ? 'idle' : 'disabled',
      },
    ],
  })

  const onScroll = useMenuScroll(true)

  if (!isMastodon) {
    return (
      <ScreenScrollContainer variant="scroll" header="Add members">
        <YStack px="$5" pt="$4" gap="$2">
          <BodyText tone="secondary">
            Lists are a Mastodon-only feature. Switch to a Mastodon account.
          </BodyText>
        </YStack>
      </ScreenScrollContainer>
    )
  }

  if (isFetching && !isFetchingNextPage && flattened.length === 0 && !query) {
    return <LoadingView />
  }
  if (error) return <ErrorState onRetry={refetch} />

  return (
    <ScreenScrollContainer
      variant="flashlist"
      header={
        list ? (
          <EmojiText
            text={list.title}
            fontSize="$7"
            fontWeight="700"
            allowFontScaling={false}
            color="$color"
            numberOfLines={1}
          />
        ) : (
          'Add members'
        )
      }
    >
      {(scrollProps) => (
        <FlashList
          data={flattened}
          keyExtractor={(item) => item.id}
          renderItem={({ item }) => (
            <Pressable
              onPress={() => toggle(item)}
              accessibilityRole="checkbox"
              accessibilityState={{ checked: selected.has(item.id) }}
              style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1 })}
            >
              <XStack alignItems="center">
                <View flex={1}>
                  <UserRow
                    account={item as any}
                    action={
                      <Feather
                        name={selected.has(item.id) ? 'check-circle' : 'circle'}
                        size={22}
                        color={
                          selected.has(item.id)
                            ? theme.interactiveActive.val
                            : theme.colorTertiary.val
                        }
                      />
                    }
                  />
                </View>
              </XStack>
            </Pressable>
          )}
          ItemSeparatorComponent={() => <Separator borderColor="$borderColor" />}
          ListEmptyComponent={
            <YStack px="$5" pt="$8" alignItems="center" gap="$2">
              <FootnoteText tone="secondary">
                {query
                  ? `No follows match “${query}”.`
                  : 'No one to add. Follow some accounts first.'}
              </FootnoteText>
            </YStack>
          }
          ListFooterComponent={<ListFooterIndicator loading={isFetchingNextPage} />}
          onEndReached={() => {
            if (hasNextPage && !isFetchingNextPage) fetchNextPage()
          }}
          onEndReachedThreshold={0.5}
          onScroll={onScroll}
          contentInsetAdjustmentBehavior={scrollProps.contentInsetAdjustmentBehavior}
          contentInset={scrollProps.contentInset}
          scrollIndicatorInsets={scrollProps.scrollIndicatorInsets}
          contentContainerStyle={{
            ...scrollProps.contentContainerStyle,
            flexGrow: 1,
          }}
        />
      )}
    </ScreenScrollContainer>
  )
}
