import Feather from '@expo/vector-icons/Feather'
import { useQuery } from '@tanstack/react-query'
import { type ErrorBoundaryProps, router } from 'expo-router'
import type { ReactElement } from 'react'
import { ActivityIndicator } from 'react-native'
import { ErrorState } from 'src/components/common/ErrorState'
import { Switch } from 'src/components/form/Switch'
import { ScreenScrollContainer } from 'src/components/layout'
import { SectionHeader, SettingRow } from 'src/components/ui'
import { useUpdateCredentialsMutation } from 'src/hooks/mutations/useUpdateCredentialsMutation'
import { useConfirmDialog } from 'src/hooks/useConfirmDialog'
import type { LoginUserResponse, Visibility } from 'src/lib/api-types'
import { haptic } from 'src/lib/haptics'
import { accountQueries } from 'src/lib/queries/accounts'
import { AccountStore } from 'src/state/accountStore'
import { Separator, Text, useTheme, View, YStack } from 'tamagui'

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

interface RenderSwitchProps {
  title: string
  description: string
  initialValue: boolean
  onCheckedChange: (checked: boolean) => void
}

// Settings rows in this screen used to be ~30 lines of `Card` + `YStack` +
// `Switch` boilerplate per occurrence; the SettingRow primitive collapses
// the geometry to a single composition with a `trailing` slot. Same haptic
// hook around onCheckedChange so toggles still tap-feedback.
const RenderSwitch = (props: RenderSwitchProps) => {
  return (
    <YStack>
      <SettingRow
        title={props.title}
        description={props.description}
        trailing={
          <Switch
            size="$3"
            defaultChecked={props.initialValue}
            onCheckedChange={(checked) => {
              haptic.selection()
              props.onCheckedChange(checked)
            }}
          >
            <Switch.Thumb transition="quicker" />
          </Switch>
        }
      />
      <Separator borderColor="$borderColor" />
    </YStack>
  )
}

interface RadioRowProps {
  title: string
  description: string
  selected: boolean
  onSelect: () => void
}

const RadioRow = (props: RadioRowProps) => {
  const theme = useTheme()
  return (
    <SettingRow
      title={props.title}
      description={props.description}
      onPress={props.onSelect}
      accessibility={{
        role: 'radio',
        label: props.title,
        hint: props.description,
        state: { selected: props.selected },
      }}
      trailing={
        props.selected ? (
          <Feather name="check" size={20} color={theme.interactiveActive.val as string} />
        ) : null
      }
    />
  )
}

type VisibilityOption = {
  value: Extract<Visibility, 'public' | 'unlisted' | 'private' | 'direct'>
  label: string
  description: string
}

// Mastodon Pass 3 Phase 2 §3.2 — the four `source[privacy]` choices Mastodon
// accepts. `draft` is in the wider `Visibility` union but is a Pixelfed-only
// post-state value, not a default-visibility setting; it's deliberately not
// surfaced here.
const VISIBILITY_OPTIONS: VisibilityOption[] = [
  {
    value: 'public',
    label: 'Public',
    description: 'New posts are visible to everyone and appear on public timelines.',
  },
  {
    value: 'unlisted',
    label: 'Unlisted',
    description: 'New posts are visible to everyone, but excluded from public timelines.',
  },
  {
    value: 'private',
    label: 'Followers only',
    description: 'New posts are only visible to your approved followers.',
  },
  {
    value: 'direct',
    label: 'Mentioned only',
    description:
      'New posts are only visible to mentioned accounts. Use sparingly — not a substitute for end-to-end encryption.',
  },
]

export default function Page() {
  const account = AccountStore.activeAccountOrThrow()
  const instance = account.instance.toLowerCase()
  const accountType = account.type
  const apiVersionsMastodon = account.apiVersionsMastodon
  const theme = useTheme()
  const { confirm, dialog: confirmDialog } = useConfirmDialog()

  // The unified credentials mutation does the optimistic flip + reconcile
  // for us, so the toggles snap immediately instead of waiting on the
  // `update_credentials` round-trip — same UX as the notification-policy
  // screen post-`useCacheMutation`.
  const updateMutation = useUpdateCredentialsMutation()

  const privateAccountSwitch = async (checked: boolean) => {
    const ok = await confirm({
      title: 'Confirm',
      message: !checked
        ? 'Are you sure you want to make your account public?'
        : 'Are you sure you want to make your account private?',
      confirmLabel: 'Confirm',
      destructive: true,
    })
    if (ok) {
      updateMutation.mutate({ locked: checked })
    } else {
      router.back()
    }
  }

  const {
    isPending,
    isLoading,
    isError,
    error,
    data: profile,
  } = useQuery(accountQueries.self())

  if (isPending || isLoading) {
    return (
      <View p="$4">
        <ActivityIndicator color={theme.color.val} />
      </View>
    )
  }

  if (isError) {
    return (
      <View p="$4">
        <Text>Error: {error?.message}</Text>
      </View>
    )
  }

  // Mastodon Pass 3 Phase 2 §3.2 — Mastodon accounts get a different,
  // server-supported subset. Pixelfed branch is byte-identical to before.
  if (accountType === 'mastodon') {
    return (
      <MastodonPrivacyBody
        profile={profile}
        apiVersionsMastodon={apiVersionsMastodon}
        confirmDialog={confirmDialog}
        onPrivateChange={privateAccountSwitch}
        onMutate={(data) => updateMutation.mutate(data)}
      />
    )
  }

  return (
    <ScreenScrollContainer variant="scroll">
      <YStack gap="$1">
        <RenderSwitch
          title="Private Account"
          description="Limit your posts and account visibility to your followers, and curate new
                follow requests"
          initialValue={profile.locked}
          onCheckedChange={(checked) => privateAccountSwitch(checked)}
        />

        <RenderSwitch
          title="Hide Followers"
          description="Hide your followers collection, only you will be able to see who follows
                you"
          initialValue={!profile.settings.show_profile_follower_count}
          onCheckedChange={(checked) =>
            updateMutation.mutate({ show_profile_follower_count: !checked })
          }
        />

        <RenderSwitch
          title="Hide Following"
          description="Hide your following collection, only you will be able to see who you are
                following"
          initialValue={!profile.settings.show_profile_following_count}
          onCheckedChange={(checked) =>
            updateMutation.mutate({ show_profile_following_count: !checked })
          }
        />

        <RenderSwitch
          title="Allow Discovery"
          description="Allow your account and posts to be recommended to other accounts"
          initialValue={profile.settings.is_suggestable}
          onCheckedChange={(checked) =>
            updateMutation.mutate({ is_suggestable: checked })
          }
        />

        <RenderSwitch
          title="Filter DMs"
          description="Filter Direct Messages from accounts you don't follow"
          initialValue={!profile.settings.public_dm}
          onCheckedChange={(checked) => updateMutation.mutate({ public_dm: !checked })}
        />

        <RenderSwitch
          title="Disable Search Engine indexing"
          description="When your account is visible to search engines, your information can be
                crawled and stored by search engines"
          initialValue={!profile.settings.crawlable}
          onCheckedChange={(checked) => updateMutation.mutate({ crawlable: !checked })}
        />

        <RenderSwitch
          title="Disable embeds"
          description="Disable profile and post embeds to prevent you or others from embedding on
                other websites"
          initialValue={profile.settings.disable_embeds}
          onCheckedChange={(checked) =>
            updateMutation.mutate({ disable_embeds: checked })
          }
        />

        <RenderSwitch
          title="Atom Feed"
          description={`Enable your public Atom feed, available at ${instance}/users/${profile?.username}.atom`}
          initialValue={profile.settings.show_atom}
          onCheckedChange={(checked) => updateMutation.mutate({ show_atom: checked })}
        />
      </YStack>
      {confirmDialog}
    </ScreenScrollContainer>
  )
}

interface MastodonPrivacyBodyProps {
  profile: LoginUserResponse
  apiVersionsMastodon: number | null
  confirmDialog: ReactElement | null
  onPrivateChange: (checked: boolean) => void
  // The new `UpdateCredentialsInput` constrains `source[privacy]` to the
  // four values Mastodon's `update_credentials` actually accepts (no
  // Pixelfed-only `'draft'` from the wider `Visibility` union). Mirror that
  // shape here so a future caller can't sneak `'draft'` past the type
  // checker.
  onMutate: (data: {
    locked?: boolean
    discoverable?: boolean
    hide_collections?: boolean
    indexable?: boolean
    'source[privacy]'?: 'public' | 'unlisted' | 'private' | 'direct'
  }) => void
}

function MastodonPrivacyBody({
  profile,
  apiVersionsMastodon,
  confirmDialog,
  onPrivateChange,
  onMutate,
}: MastodonPrivacyBodyProps) {
  // Mastodon's `update_credentials` accepts `indexable` from 4.2+; the
  // `apiVersionsMastodon` field captured at preflight is the integer from
  // `/api/v2/instance.api_versions.mastodon`. Hide the row on instances
  // that pre-date the field rather than risk a 422 on save.
  const indexableSupported = (apiVersionsMastodon ?? 0) >= 1

  // Account.source.privacy is required on the type but a defensive default
  // costs nothing — a fork that drops `source` would otherwise crash the
  // page on first paint. Same defensive read on `hide_collections`, which
  // is optional on the type for Pixelfed compatibility.
  const currentVisibility: Visibility = profile.source?.privacy ?? 'public'
  const hideCollections = profile.hide_collections ?? false

  return (
    <ScreenScrollContainer variant="scroll">
      <YStack gap="$1">
        <RenderSwitch
          title="Manually approve followers"
          description="New follow requests must be approved before the follower sees your posts."
          initialValue={profile.locked}
          onCheckedChange={(checked) => onPrivateChange(checked)}
        />

        <RenderSwitch
          title="Suggest account to others"
          description="Allow your account to be recommended in your server's directory and trending suggestions."
          initialValue={profile.discoverable ?? false}
          onCheckedChange={(checked) => onMutate({ discoverable: checked })}
        />

        <RenderSwitch
          title="Hide following and followers"
          description="Hide your follows and followers collections from your public profile."
          initialValue={hideCollections}
          onCheckedChange={(checked) => onMutate({ hide_collections: checked })}
        />

        {indexableSupported ? (
          <RenderSwitch
            title="Allow search engines to index"
            description="When enabled, your public profile and posts may be indexed by search engines."
            initialValue={profile.indexable ?? false}
            onCheckedChange={(checked) => onMutate({ indexable: checked })}
          />
        ) : null}
      </YStack>

      <SectionHeader spacing="loose">Default post visibility</SectionHeader>
      <YStack gap="$1">
        {VISIBILITY_OPTIONS.map((opt, idx) => (
          <YStack key={opt.value}>
            <RadioRow
              title={opt.label}
              description={opt.description}
              selected={currentVisibility === opt.value}
              onSelect={() => {
                if (currentVisibility === opt.value) return
                haptic.selection()
                onMutate({ 'source[privacy]': opt.value })
              }}
            />
            {idx < VISIBILITY_OPTIONS.length - 1 ? (
              <Separator borderColor="$borderColor" />
            ) : null}
          </YStack>
        ))}
      </YStack>

      {confirmDialog}
    </ScreenScrollContainer>
  )
}
