import type { BottomSheetModal } from '@gorhom/bottom-sheet'
import { useQuery } from '@tanstack/react-query'
import type { ErrorBoundaryProps } from 'expo-router'
import { useMemo, useRef, useState } from 'react'
import { Pressable } from 'react-native'
import { ActionSheet } from 'src/components/common/ActionSheet'
import { ErrorState } from 'src/components/common/ErrorState'
import UserAvatar from 'src/components/common/UserAvatar'
import { ScreenScrollContainer } from 'src/components/layout'
import { useScreenMenu } from 'src/components/menu'
import { FootnoteText, FormField, SectionHeader, SubheadText } from 'src/components/ui'
import {
  useDeleteAvatarMutation,
  useUpdateCredentialsMutation,
} from 'src/hooks/mutations/useUpdateCredentialsMutation'
import { useInstanceCapabilities } from 'src/lib/capabilities'
import { haptic } from 'src/lib/haptics'
import { pickMedia, resizeImage, toUploadFile } from 'src/lib/media'
import { settingsQueries } from 'src/lib/queries/settings'
import { useActiveProfile } from 'src/state/AuthProvider'
import { Button, Spinner, useTheme, View, XStack, YStack } from 'tamagui'

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

const NAME_MAX = 30
const WEBSITE_MAX = 120
const BIO_FALLBACK_MAX = 500

// Inline editor for the active account's profile — supersedes the routed
// updateName / updateBio / updateWebsite / ProfileFieldEditor flow. Avatar,
// display name, bio, and website all save together through the unified
// `updateCredentials` mutation hook, which in turn handles optimistic
// updates, reconcile, and the deferred re-fetch for Pixelfed's async
// `AvatarOptimize` job. The bottom sheet for "Change / Delete" stays — same
// pattern as the post / chat-message kebabs — but is now driven by the
// `useDeleteAvatarMutation` companion hook so success/error toasts and
// haptics are uniform with the rest of the settings family.
function isMissingAvatar(url: string | null | undefined): boolean {
  if (!url) return true
  // Pixelfed CDN: …/default.jpg, …/default-2.jpg, …/default-3.jpg.
  // Mastodon: /avatars/original/missing.png.
  // Match on the path tail rather than a `.includes('default.')` substring
  // search so unrelated URLs that happen to contain the substring don't
  // false-positive.
  return /\/(?:default(?:-\d+)?\.(?:jpg|jpeg|png)|missing\.png)$/i.test(url)
}

export default function ProfilePage() {
  const theme = useTheme()
  const { profile: user, isFetching } = useActiveProfile()
  const photoMenuRef = useRef<BottomSheetModal>(null)

  const { data: serverConfig } = useQuery(settingsQueries.config())
  const capabilities = useInstanceCapabilities()
  const bioMax = useMemo(() => {
    const raw = serverConfig?.account?.max_bio_length
    return typeof raw === 'number' && raw > 0 ? Math.floor(raw) : BIO_FALLBACK_MAX
  }, [serverConfig])

  const initialName = user?.display_name ?? ''
  // Editable raw-text bio: Pixelfed's verify_credentials populates the
  // `note_text` mirror (HTML stripped); Mastodon stamps the same raw form on
  // `source.note`. The public-account refetch omits both — without this
  // fallback chain the field renders empty for Mastodon accounts.
  const initialNote = user?.note_text ?? user?.source?.note ?? ''
  const initialWebsite = user?.website ?? ''

  const [name, setName] = useState(initialName)
  const [note, setNote] = useState(initialNote)
  const [website, setWebsite] = useState(initialWebsite)

  const dirty = name !== initialName || note !== initialNote || website !== initialWebsite

  const updateMutation = useUpdateCredentialsMutation({
    onSuccess: () => {
      // Edit Profile is a single screen, so we don't navigate on save —
      // just let the cache reconcile re-render the form with the server
      // truth. The success haptic + toast inside the hook is the user
      // signal.
    },
  })
  const deleteAvatarMutation = useDeleteAvatarMutation()

  const onSave = () => {
    if (!dirty || updateMutation.isPending) return
    haptic.selection()
    updateMutation.mutate({
      display_name: name,
      note,
      website,
    })
  }

  const pickAvatar = async () => {
    const raws = await pickMedia({ kinds: ['photo'], squareCrop: true, limit: 1 })
    if (!raws.length) return
    // Mastodon's avatar endpoint allowlist is jpeg/png/gif/webp (no HEIC),
    // and Pixelfed avatars inherit the instance `media_types` config — both
    // narrower than the post pipeline. Reuse the shared resize/transcode so
    // an iOS-default HEIC pick lands as JPEG (or WebP on instances that
    // publish it). 2 MB cap matches the typical server-side avatar limit.
    const normalized = await resizeImage(raws[0], {
      maxImageBytes: 2 * 1024 * 1024,
      maxImageWidth: 1024,
      supportedImageMimeTypes: capabilities.supportedImageMimeTypes,
    })
    const file = toUploadFile({ uri: normalized.uri, mimeType: normalized.mimeType })
    updateMutation.mutate({ avatar: file })
  }

  const openPhotoSheet = () => {
    haptic.selection()
    if (isMissingAvatar(user?.avatar)) {
      // First-time upload — skip the menu, jump to the picker.
      pickAvatar()
      return
    }
    photoMenuRef.current?.present()
  }

  const closePhotoMenu = () => photoMenuRef.current?.close()

  useScreenMenu({
    title: 'Edit Profile',
    intents: [
      { kind: 'back' },
      {
        kind: 'commit',
        id: 'save-profile',
        icon: 'check',
        label: 'Save profile',
        onPress: onSave,
        state: updateMutation.isPending ? 'busy' : dirty ? 'idle' : 'disabled',
      },
    ],
  })

  return (
    <ScreenScrollContainer variant="scroll" header="Edit Profile">
      <YStack px="$5" pt="$4" pb="$2" gap="$4">
        <XStack alignItems="center" gap="$4">
          <UserAvatar url={user?.avatar} size="lg" accessibilityLabel={user?.username} />
          <YStack flex={1} gap="$1">
            <SubheadText emphasis="strong">@{user?.username}</SubheadText>
            <Pressable onPress={openPhotoSheet} accessibilityRole="button">
              <FootnoteText tone="link" emphasis="strong">
                {isMissingAvatar(user?.avatar)
                  ? 'Upload profile photo'
                  : 'Update or delete profile photo'}
              </FootnoteText>
            </Pressable>
          </YStack>
          {isFetching || updateMutation.isPending ? (
            <Spinner color={theme.colorTertiary.val} />
          ) : null}
        </XStack>
      </YStack>

      <SectionHeader>Profile</SectionHeader>

      <YStack px="$5" gap="$4" pb="$4">
        <FormField
          label="Name"
          value={name}
          onChangeText={setName}
          maxLength={NAME_MAX}
          placeholder="Add your full name, or nickname"
          helper="Help people discover your account by using the name you're known by: either your full name, nickname, or business name."
        />
        <FormField
          label="Bio"
          value={note}
          onChangeText={setNote}
          maxLength={bioMax}
          multiline
          placeholder="Add an optional bio"
          helper="Mentions and hashtags will be linked. Use full webfinger addresses for remote accounts like @pixelfed@mastodon.social."
        />
        <FormField
          label="Website"
          value={website}
          onChangeText={setWebsite}
          maxLength={WEBSITE_MAX}
          placeholder="example.com"
          keyboardType="url"
          autoCapitalize="none"
          autoCorrect={false}
          helper="Add an optional website to your profile that is publicly visible."
        />
      </YStack>

      <View px="$5" pt="$2" pb="$8">
        <Button
          size="$5"
          bg={theme.interactiveActive.val}
          disabled={!dirty || updateMutation.isPending}
          opacity={!dirty || updateMutation.isPending ? 0.5 : 1}
          onPress={onSave}
          accessibilityLabel="Save changes"
          pressStyle={{ opacity: 0.8 }}
        >
          <Button.Text color={theme.background.val} fontWeight="700" fontSize="$5">
            {updateMutation.isPending ? 'Saving…' : 'Save changes'}
          </Button.Text>
        </Button>
      </View>

      <ActionSheet
        ref={photoMenuRef}
        title="Change Profile Photo"
        description="Upload a new photo or delete your existing photo. It may take a few minutes to update."
        items={[
          {
            id: 'change',
            label: 'Change Photo',
            onPress: () => {
              closePhotoMenu()
              pickAvatar()
            },
          },
          {
            id: 'delete',
            label: 'Delete',
            destructive: true,
            onPress: () => {
              closePhotoMenu()
              deleteAvatarMutation.mutate()
            },
          },
        ]}
        onCancel={closePhotoMenu}
      />
    </ScreenScrollContainer>
  )
}
