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 { useEffect, useMemo, useRef, useState } from 'react'
import { Pressable } from 'react-native'
import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable'
import EmojiText from 'src/components/common/EmojiText'
import { ErrorState } from 'src/components/common/ErrorState'
import { LoadingView } from 'src/components/common/LoadingView'
import { UserRow } from 'src/components/common/UserRow'
import { Switch } from 'src/components/form/Switch'
import { ScreenScrollContainer } from 'src/components/layout'
import { useScreenMenu } from 'src/components/menu'
import {
  BodyText,
  FootnoteText,
  FormField,
  LinkRowGroup,
  SectionHeader,
  SettingRow,
} from 'src/components/ui'
import { useDeleteMastodonListMutation } from 'src/hooks/mutations/useDeleteMastodonListMutation'
import { useRemoveListMembersMutation } from 'src/hooks/mutations/useRemoveListMembersMutation'
import { useUpdateMastodonListMutation } from 'src/hooks/mutations/useUpdateMastodonListMutation'
import { useConfirmDialog } from 'src/hooks/useConfirmDialog'
import type { MastodonList } from 'src/lib/api/mastodon'
import type { Account } from 'src/lib/api-types'
import { haptic } from 'src/lib/haptics'
import { listsQueries } from 'src/lib/queries/lists'
import { useAuth } from 'src/state/AuthProvider'
import {
  Separator,
  Text,
  useTheme,
  useToastController,
  View,
  XStack,
  YStack,
} from 'tamagui'

type RepliesPolicy = 'followed' | 'list' | 'none'

const REPLIES_POLICY_OPTIONS: ReadonlyArray<{
  value: RepliesPolicy
  label: string
  description: string
}> = [
  {
    value: 'list',
    label: 'List members',
    description: 'Show replies to other members of this list.',
  },
  {
    value: 'followed',
    label: 'People you follow',
    description: 'Show replies to anyone you follow.',
  },
  { value: 'none', label: 'No one', description: 'Hide replies in the list timeline.' },
]
const TITLE_MAX = 50

// Surface B — Per-list detail / edit. Routed under `/settings/lists/[id]` as
// a normal stack screen so the system back button, status bar, and the
// existing menu intent vocabulary all behave canonically.
//
// Editing model: title rename auto-commits on blur, the replies-policy
// rows + Exclusive switch commit on change. Optimistic mutation handles
// cache reconcile —
// the screen reads back from the catalogue (`listsQueries.lists()`) so there
// is no risk of UI showing two truths after a server normalize.
export default function ListDetailScreen() {
  const theme = useTheme()
  const toast = useToastController()
  const { confirm, dialog } = useConfirmDialog()
  const { id: listId } = useLocalSearchParams<{ id: string }>()
  const { user } = useAuth()
  const isMastodon = user?.type === 'mastodon'

  const {
    data: lists,
    isPending: listsPending,
    error: listsError,
    refetch: refetchLists,
  } = useQuery({ ...listsQueries.lists(), enabled: isMastodon && !!listId })
  const list = useMemo(() => lists?.find((l) => l.id === listId) ?? null, [lists, listId])

  const update = useUpdateMastodonListMutation()
  const removeMembers = useRemoveListMembersMutation()
  const deleteList = useDeleteMastodonListMutation()

  const [titleDraft, setTitleDraft] = useState('')
  const initialTitleRef = useRef<string | null>(null)
  // Seed the input once when the canonical title first arrives. Subsequent
  // server reconciles update the row in cache but should not stomp the
  // user's in-progress edit — that would be infuriating.
  useEffect(() => {
    if (initialTitleRef.current === null && list?.title) {
      initialTitleRef.current = list.title
      setTitleDraft(list.title)
    }
  }, [list?.title])

  const onTitleBlur = () => {
    if (!list) return
    const next = titleDraft.trim()
    if (!next || next === list.title) {
      setTitleDraft(list.title)
      return
    }
    if (next.length > TITLE_MAX) return
    update.mutate(
      { listId: list.id, patch: { title: next } },
      {
        onError: () => {
          toast.show('Could not save name', { preset: 'error', duration: 2500 })
          setTitleDraft(list.title)
        },
      }
    )
  }

  const onRepliesPolicyChange = (v: RepliesPolicy) => {
    if (!list || v === list.replies_policy) return
    haptic.selection()
    update.mutate(
      { listId: list.id, patch: { repliesPolicy: v } },
      {
        onError: () =>
          toast.show('Could not update list', { preset: 'error', duration: 2500 }),
      }
    )
  }

  const onExclusiveChange = (checked: boolean) => {
    if (!list || checked === list.exclusive) return
    haptic.selection()
    update.mutate(
      { listId: list.id, patch: { exclusive: checked } },
      {
        onError: () =>
          toast.show('Could not update list', { preset: 'error', duration: 2500 }),
      }
    )
  }

  const onDeleteList = async () => {
    if (!list) return
    const confirmed = await confirm({
      title: 'Delete list?',
      message: `“${list.title}” will be removed. Members are not unfollowed.`,
      confirmLabel: 'Delete',
      destructive: true,
    })
    if (!confirmed) return
    deleteList.mutate(
      { listId: list.id },
      {
        onSuccess: () => router.back(),
        onError: () =>
          toast.show('Could not delete list', { preset: 'error', duration: 2500 }),
      }
    )
  }

  useScreenMenu({
    title: list?.title ?? 'List',
    intents: [{ kind: 'back' }],
  })

  if (!isMastodon) {
    return (
      <ScreenScrollContainer variant="scroll" header="List">
        <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 (listsPending) return <LoadingView />
  if (listsError) return <ErrorState onRetry={refetchLists} />
  if (!list) {
    return (
      <ScreenScrollContainer variant="scroll" header="List">
        <YStack px="$5" pt="$4" gap="$2">
          <BodyText tone="secondary">This list no longer exists.</BodyText>
        </YStack>
      </ScreenScrollContainer>
    )
  }

  return (
    <>
      <ScreenScrollContainer
        variant="flashlist"
        header={
          <EmojiText
            text={list.title}
            fontSize="$7"
            fontWeight="700"
            allowFontScaling={false}
            color="$color"
            numberOfLines={1}
          />
        }
      >
        {(scrollProps) => (
          <MembersFlashList
            listId={list.id}
            listTitle={list.title}
            scrollProps={scrollProps}
            header={
              <ListHeader
                list={list}
                titleDraft={titleDraft}
                setTitleDraft={setTitleDraft}
                onTitleBlur={onTitleBlur}
                onRepliesPolicyChange={onRepliesPolicyChange}
                onExclusiveChange={onExclusiveChange}
              />
            }
            footer={<ListFooter onDelete={onDeleteList} pending={deleteList.isPending} />}
            onRemove={(account) => {
              removeMembers.mutate(
                { listId: list.id, accountIds: [account.id] },
                {
                  onError: () =>
                    toast.show('Could not remove from list', {
                      preset: 'error',
                      duration: 2500,
                    }),
                }
              )
            }}
            theme={theme}
          />
        )}
      </ScreenScrollContainer>
      {dialog}
    </>
  )
}

function ListHeader({
  list,
  titleDraft,
  setTitleDraft,
  onTitleBlur,
  onRepliesPolicyChange,
  onExclusiveChange,
}: {
  list: MastodonList
  titleDraft: string
  setTitleDraft: (next: string) => void
  onTitleBlur: () => void
  onRepliesPolicyChange: (v: RepliesPolicy) => void
  onExclusiveChange: (checked: boolean) => void
}) {
  const theme = useTheme()
  return (
    <YStack pt="$4" gap="$2">
      <YStack px="$5" gap="$4">
        <FormField
          label="Name"
          value={titleDraft}
          onChangeText={setTitleDraft}
          onBlur={onTitleBlur}
          maxLength={TITLE_MAX}
          enterKeyHint="done"
          autoCapitalize="words"
        />
      </YStack>

      <SectionHeader>Show replies to</SectionHeader>
      {REPLIES_POLICY_OPTIONS.map((opt, idx) => {
        const selected = list.replies_policy === opt.value
        return (
          <YStack key={opt.value}>
            <SettingRow
              title={opt.label}
              description={opt.description}
              onPress={() => onRepliesPolicyChange(opt.value)}
              accessibility={{
                role: 'radio',
                label: opt.label,
                hint: opt.description,
                state: { selected },
              }}
              trailing={
                selected ? (
                  <Feather name="check" size={20} color={theme.interactiveActive.val} />
                ) : null
              }
              tone="secondary"
            />
            {idx < REPLIES_POLICY_OPTIONS.length - 1 ? (
              <Separator borderColor="$borderColor" />
            ) : null}
          </YStack>
        )
      })}

      <SectionHeader>Options</SectionHeader>
      <View px="$5">
        <SettingRow
          title="Exclusive"
          description="Hide members' posts from the home timeline."
          trailing={
            <Switch
              size="$3"
              checked={list.exclusive}
              onCheckedChange={onExclusiveChange}
            >
              <Switch.Thumb transition="quicker" />
            </Switch>
          }
        />
      </View>

      <LinkRowGroup
        header="Members"
        rows={[
          {
            kind: 'internal',
            icon: 'user-plus',
            title: 'Add members',
            path: `/settings/lists/${list.id}/add` as any,
          },
        ]}
      />
    </YStack>
  )
}

function MembersFlashList({
  listId,
  listTitle,
  header,
  footer,
  onRemove,
  theme,
  scrollProps,
}: {
  listId: string
  listTitle: string
  header: React.ReactNode
  footer: React.ReactNode
  onRemove: (account: Account) => void
  theme: ReturnType<typeof useTheme>
  scrollProps: {
    contentInsetAdjustmentBehavior: 'never'
    contentInset: { top: number; bottom: number }
    scrollIndicatorInsets: { top: number; bottom: number }
    contentContainerStyle: { paddingTop: number; paddingBottom: number }
  }
}) {
  const {
    data: members,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    isPending,
    error,
    refetch,
  } = useInfiniteQuery(listsQueries.accounts(listId))

  const flattened = useMemo<Account[]>(
    () => (members?.pages.flatMap((p) => p.data) ?? []) as Account[],
    [members]
  )

  return (
    <FlashList
      data={flattened}
      keyExtractor={(item) => item.id}
      ListHeaderComponent={header as React.ReactElement}
      ListFooterComponent={footer as React.ReactElement}
      renderItem={({ item }) => (
        <MemberRow account={item} onRemove={() => onRemove(item)} />
      )}
      ItemSeparatorComponent={() => <Separator borderColor="$borderColor" />}
      ListEmptyComponent={
        isPending ? (
          <YStack px="$5" pt="$3" alignItems="center">
            <FootnoteText tone="secondary">Loading…</FootnoteText>
          </YStack>
        ) : error ? (
          <YStack px="$5" pt="$3">
            <ErrorState onRetry={refetch} />
          </YStack>
        ) : (
          <YStack px="$5" pt="$3" gap="$2" alignItems="center">
            <Feather name="users" size={32} color={theme.colorTertiary.val as string} />
            <XStack alignItems="center" gap={4} flexWrap="wrap" justifyContent="center">
              <FootnoteText tone="secondary">No one in</FootnoteText>
              <EmojiText
                text={`“${listTitle}”`}
                fontSize="$subhead"
                color="$colorSecondary"
              />
              <FootnoteText tone="secondary">yet.</FootnoteText>
            </XStack>
          </YStack>
        )
      }
      onEndReached={() => {
        if (hasNextPage && !isFetchingNextPage) fetchNextPage()
      }}
      onEndReachedThreshold={0.5}
      contentInsetAdjustmentBehavior={scrollProps.contentInsetAdjustmentBehavior}
      contentInset={scrollProps.contentInset}
      scrollIndicatorInsets={scrollProps.scrollIndicatorInsets}
      contentContainerStyle={{
        ...scrollProps.contentContainerStyle,
        flexGrow: 1,
      }}
    />
  )
}

function MemberRow({ account, onRemove }: { account: Account; onRemove: () => void }) {
  const theme = useTheme()
  const renderRightActions = () => (
    <Pressable
      onPress={onRemove}
      accessibilityRole="button"
      accessibilityLabel={`Remove @${account.acct} from list`}
      style={{
        backgroundColor: theme.colorDanger.val as string,
        justifyContent: 'center',
        alignItems: 'center',
        paddingHorizontal: 24,
      }}
    >
      <Text color={theme.onAccent.val as string} fontWeight="600" fontSize="$3">
        Remove
      </Text>
    </Pressable>
  )
  return (
    <Swipeable
      renderRightActions={renderRightActions}
      friction={2}
      rightThreshold={40}
      overshootRight={false}
    >
      <View bg="$background">
        <UserRow account={account as any} />
      </View>
    </Swipeable>
  )
}

function ListFooter({ onDelete, pending }: { onDelete: () => void; pending: boolean }) {
  return (
    <YStack opacity={pending ? 0.6 : 1} pointerEvents={pending ? 'none' : 'auto'}>
      <LinkRowGroup
        rows={[
          {
            kind: 'action',
            icon: 'trash-2',
            title: 'Delete list',
            tone: 'danger',
            onPress: onDelete,
          },
        ]}
      />
    </YStack>
  )
}
