import { Feather } from '@expo/vector-icons'
import type { BottomSheetModal } from '@gorhom/bottom-sheet'
import { useAuth } from '@state/AuthProvider'
import { useRouter } from 'expo-router'
import { useRef } from 'react'
import { Pressable } from 'react-native'
import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable'
import { AccentPicker } from 'src/components/account/AccentPicker'
import UserAvatar from 'src/components/common/UserAvatar'
import { ScreenScrollContainer } from 'src/components/layout'
import { BodyText, FootnoteText, SubheadText } from 'src/components/ui'
import { useConfirmDialog } from 'src/hooks/useConfirmDialog'
import { useStoredTheme } from 'src/hooks/useStoredTheme'
import { ACCENT_VARIANTS, type AccentVariantId } from 'src/lib/accentVariants'
import {
  AccountStore,
  type AccountSummary,
  fullHandle,
  MAX_ACCOUNTS,
} from 'src/state/accountStore'
import {
  Button,
  Group,
  Separator,
  Text,
  useTheme,
  useToastController,
  View,
  XStack,
  YStack,
} from 'tamagui'

export default function AccountsScreen() {
  const { accounts, activeAccountId, switchAccount, removeAccount, signOutAll } =
    useAuth()
  const { confirm, dialog: confirmDialog } = useConfirmDialog()
  const router = useRouter()
  const theme = useTheme()
  const toast = useToastController()
  const accentPickerRef = useRef<BottomSheetModal>(null)

  const atCap = accounts.length >= MAX_ACCOUNTS
  const activeAccount = accounts.find((a) => a.accountId === activeAccountId) ?? null

  const goAddAccount = () => {
    router.push({ pathname: '/handleLogin', params: { addAccount: '1' } })
  }

  const goReauth = (account: AccountSummary) => {
    // Re-auth flow: same screen as Add Account, with the instance pre-filled.
    // The existing-(instance,userId) match in the login callback (§6.2 step 7)
    // preserves the accountId and clears `needsReauth` automatically — no
    // extra wiring on this side.
    router.push({
      pathname: '/handleLogin',
      params: { addAccount: '1', server: account.instance },
    })
  }

  const onRowPress = (account: AccountSummary) => {
    if (account.needsReauth) {
      goReauth(account)
      return
    }
    if (account.accountId === activeAccountId) return
    switchAccount(account.accountId)
  }

  const onRemove = async (account: AccountSummary) => {
    const ok = await confirm({
      title: `Sign out of ${fullHandle(account)}?`,
      message:
        'This removes the account from this device. Your posts and follows on the server are not affected.',
      confirmLabel: 'Sign out',
      destructive: true,
    })
    if (!ok) return
    const handle = fullHandle(account)
    await removeAccount(account.accountId)
    toast.show(`Signed out of ${handle}`, { preset: 'success' })
    // `markSignedOut` clears the active pointer when no fallback exists.
    // The Accounts screen sits outside `Stack.Protected guard={!!user}`
    // (see _layout.tsx), so without an explicit redirect the user would
    // be stranded here with an empty list.
    if (!AccountStore.activeId()) router.replace('/(public)/login' as any)
  }

  const onSignOutAll = async () => {
    const ok = await confirm({
      title: 'Sign out of all accounts?',
      message:
        'Removes every account from this device and returns you to the sign-in screen.',
      confirmLabel: 'Sign out of all',
      destructive: true,
    })
    if (!ok) return
    await signOutAll()
    toast.show('Signed out of all accounts', { preset: 'success' })
    router.replace('/(public)/login' as any)
  }

  return (
    <ScreenScrollContainer variant="scroll" header="Accounts">
      <YStack px="$5" pt="$4" pb="$2" gap="$2">
        <BodyText tone="secondary">
          Switch between signed-in accounts or add another. Notifications, feeds, and your
          composer always reflect the active account.
        </BodyText>
      </YStack>

      <YStack px="$5" pt="$5" gap="$3">
        <FootnoteText tone="tertiary" letterSpacing={0.4} textTransform="uppercase">
          Signed in
        </FootnoteText>
        <Group orientation="vertical">
          {accounts.map((account, i) => (
            <View key={account.accountId}>
              {i > 0 ? <Separator borderColor={theme.borderColor.val} /> : null}
              <AccountRow
                account={account}
                isActive={account.accountId === activeAccountId}
                onPress={() => onRowPress(account)}
                onRemove={() => onRemove(account)}
                onAccentPress={() => accentPickerRef.current?.present()}
              />
            </View>
          ))}
        </Group>
      </YStack>

      <YStack px="$5" pt="$6" gap="$2">
        <Button
          size="$4"
          icon={<Feather name="plus" size={16} color={theme.color.val} />}
          disabled={atCap}
          opacity={atCap ? 0.5 : 1}
          onPress={goAddAccount}
        >
          <Button.Text fontWeight="600">Add account</Button.Text>
        </Button>
        {atCap ? (
          <FootnoteText tone="secondary">
            You're at the {MAX_ACCOUNTS}-account limit. Sign out of one to add another.
          </FootnoteText>
        ) : null}
      </YStack>

      <YStack px="$5" pt="$8" pb="$8">
        <Button
          variant="outlined"
          backgroundColor="transparent"
          borderColor={theme.colorDanger.val}
          onPress={onSignOutAll}
          transition="quick"
          pressStyle={{ opacity: 0.75, scale: 0.99 }}
        >
          <Text color={theme.colorDanger.val} fontWeight="600">
            Sign out of all accounts
          </Text>
        </Button>
      </YStack>

      {confirmDialog}

      {activeAccount ? (
        <AccentPicker
          ref={accentPickerRef}
          accountId={activeAccount.accountId}
          selected={activeAccount.accentVariant}
        />
      ) : null}
    </ScreenScrollContainer>
  )
}

function AccountRow({
  account,
  isActive,
  onPress,
  onRemove,
  onAccentPress,
}: {
  account: AccountSummary
  isActive: boolean
  onPress: () => void
  onRemove: () => void
  onAccentPress: () => void
}) {
  const theme = useTheme()
  const scheme = useStoredTheme()
  const accentSpec = ACCENT_VARIANTS[account.accentVariant as AccentVariantId]
  const accentHex = scheme === 'light' ? accentSpec.light : accentSpec.dark

  // Tap surface: presses are absorbed by the row, swipe gestures still
  // bubble up to ReanimatedSwipeable. `pressed` styling is intentionally
  // light — the destructive action lives behind the swipe, not behind a
  // long-press, so the tap should feel safe.
  const renderRightActions = () => (
    <Pressable
      onPress={onRemove}
      style={{
        backgroundColor: theme.colorDanger.val as string,
        justifyContent: 'center',
        alignItems: 'center',
        paddingHorizontal: 24,
      }}
      accessibilityRole="button"
      accessibilityLabel={`Sign out of ${fullHandle(account)}`}
    >
      <Text color={theme.onAccent.val as string} fontWeight="600" fontSize="$3">
        Sign out
      </Text>
    </Pressable>
  )

  return (
    <Swipeable
      renderRightActions={renderRightActions}
      friction={2}
      rightThreshold={40}
      overshootRight={false}
    >
      <Pressable
        onPress={onPress}
        style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1 })}
        accessibilityRole="button"
        accessibilityLabel={
          account.needsReauth
            ? `Re-sign in to ${fullHandle(account)}`
            : isActive
              ? `${fullHandle(account)} (active)`
              : `Switch to ${fullHandle(account)}`
        }
      >
        <XStack bg="$backgroundSecondary" px="$4" py="$3" gap="$3" alignItems="center">
          <UserAvatar
            url={account.avatarUrl}
            size="md"
            initial={account.displayName || account.username}
          />
          <YStack flex={1} gap={2}>
            <SubheadText emphasis="strong" numberOfLines={1}>
              {account.displayName || account.username}
            </SubheadText>
            <FootnoteText tone="secondary" numberOfLines={1}>
              {fullHandle(account)}
            </FootnoteText>
            <XStack gap="$2" pt={2} flexWrap="wrap">
              {isActive ? <Pill tone="positive">Active</Pill> : null}
              {account.needsReauth ? <Pill tone="warning">Re-sign-in needed</Pill> : null}
            </XStack>
          </YStack>
          {/*
            Swatch disc — visible on every row so the account's hue is always
            legible, but only the active row is tappable. Inactive rows are
            read-only by design (§5.4): the picker preview makes more sense
            when the chrome around it is currently in that account's variant.
          */}
          {isActive ? (
            <Pressable
              onPress={onAccentPress}
              hitSlop={8}
              accessibilityRole="button"
              accessibilityLabel={`Change accent colour (${accentSpec.name})`}
              style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1 })}
            >
              <View
                width={24}
                height={24}
                borderRadius={12}
                backgroundColor={accentHex}
                borderWidth={1}
                borderColor={theme.borderColorStrong.val}
              />
            </Pressable>
          ) : (
            <View
              width={24}
              height={24}
              borderRadius={12}
              backgroundColor={accentHex}
              borderWidth={1}
              borderColor={theme.borderColorStrong.val}
              opacity={0.6}
            />
          )}
          <Feather
            name={account.needsReauth ? 'alert-triangle' : 'chevron-right'}
            size={16}
            color={
              account.needsReauth
                ? (theme.colorDanger.val as string)
                : (theme.colorTertiary.val as string)
            }
          />
        </XStack>
      </Pressable>
    </Swipeable>
  )
}

function Pill({
  tone,
  children,
}: {
  tone: 'positive' | 'warning' | 'muted'
  children: string
}) {
  const theme = useTheme()
  // Tamagui v2 has no first-class Badge; the §11.4.1 prescription is an
  // XStack with theme-tinted background + small SizableText. We resolve the
  // tone tokens through the theme so light/dark both look right.
  const bg =
    tone === 'positive'
      ? (theme.accent?.val ?? theme.color.val)
      : tone === 'warning'
        ? (theme.colorDanger?.val ?? theme.color.val)
        : (theme.backgroundTertiary?.val ?? theme.background.val)
  const fg =
    tone === 'muted'
      ? (theme.colorSecondary?.val ?? theme.color.val)
      : (theme.onAccent?.val ?? theme.color.val)
  return (
    <XStack
      backgroundColor={bg as string}
      borderRadius={999}
      paddingHorizontal={8}
      paddingVertical={2}
    >
      <Text color={fg as string} fontSize={11} fontWeight="700" allowFontScaling={false}>
        {children}
      </Text>
    </XStack>
  )
}
