import type { QueryClient } from '@tanstack/react-query'
import { listScopeKeys, useCacheMutation } from 'src/hooks/mutations/cacheMutation'
import { mergeOptimisticCredentials } from 'src/hooks/mutations/mergeOptimisticCredentials'
import { type UpdateCredentialsInput, updateCredentials } from 'src/lib/api'
import { haptic } from 'src/lib/haptics'
import { accountQueries } from 'src/lib/queries/accounts'
import { AccountStore } from 'src/state/accountStore'
import { useToastController } from 'tamagui'

// Single mutation hook for `update_credentials` — covers everything the API
// supports (display_name / note / website / locked, the Pixelfed-only
// privacy switches, the Mastodon-only switches + default visibility, and
// the avatar file). Replaces the legacy `useProfileMutation` so the privacy
// toggles, the inline edit-profile form, and the avatar/delete-avatar flows
// all run through one wrapper with optimistic updates and a single retry
// pattern.
//
// The active-account profile is cached under two keys:
//   • `accountQueries.self()` — feeds privacy-settings, source-of-truth for
//     `verify_credentials` (`LoginUserResponse` shape with Pixelfed-side
//     `settings` blob).
//   • `accountQueries.selfProfile(userId)` — feeds `useActiveProfile()` (the
//     Edit Profile screen, the Profile tab header). Wire shape is `Account`.
// We snapshot, optimistically merge, and reconcile both so the UI updates
// don't depend on which screen the user is on.

function reconcileBothKeys(qc: QueryClient, server: any, userId: string) {
  // The wire response is `Account`-shaped; merge into the cached blob so we
  // don't drop `LoginUserResponse`-only fields (`is_admin`, `email`, the
  // `settings` block) that the cached value carries but the response does
  // not.
  qc.setQueryData(accountQueries.self().queryKey, (old: any) =>
    old ? { ...old, ...server } : server
  )
  if (userId) {
    qc.setQueryData(accountQueries.selfProfile(userId).queryKey, (old: any) =>
      old ? { ...old, ...server } : server
    )
  }
}

type Args = {
  /** Called once after a successful save (after reconcile, before the
   *  delayed avatar invalidate). */
  onSuccess?: () => void
}

export function useUpdateCredentialsMutation(args: Args = {}) {
  const toast = useToastController()
  // We resolve the active userId at hook-construction time. The hook is
  // remounted on account switch (route remounts under the new query
  // keyspace), so re-reading the registry is the correct shape. Tolerate a
  // null active account: the Settings → Profile screen sits outside
  // `Stack.Protected` and re-renders during sign-out before the redirect,
  // so a hard throw here surfaces as a render-error redbox; the screen is
  // about to unmount, no mutation will actually fire under the null state.
  const userId = AccountStore.activeAccount()?.userId ?? ''

  return useCacheMutation<any, UpdateCredentialsInput>({
    mutationFn: (input) => updateCredentials(input),
    snapshotKeys: () =>
      listScopeKeys(
        accountQueries.self().queryKey,
        accountQueries.selfProfile(userId).queryKey
      ),
    applyOptimistic: (qc, input) => {
      qc.setQueryData(accountQueries.self().queryKey, (old: any) =>
        mergeOptimisticCredentials(old, input)
      )
      qc.setQueryData(accountQueries.selfProfile(userId).queryKey, (old: any) =>
        mergeOptimisticCredentials(old, input)
      )
    },
    reconcile: (qc, server) => reconcileBothKeys(qc, server, userId),
    onSuccess: (_data, vars, qc) => {
      haptic.success()
      // Pixelfed dispatches `AvatarOptimize` asynchronously, so the
      // immediate response carries the *old* avatar URL. Re-fetch after a
      // short delay to pick up the optimised image. The user-facing copy
      // already warns "It may take a few minutes to update" — the delayed
      // refetch lands within that expectation. The same reasoning covers
      // an optional `delete_avatar` flow that lands here in the future.
      if (vars.avatar) {
        setTimeout(() => {
          qc.invalidateQueries({ queryKey: accountQueries.self().queryKey })
          if (userId) {
            qc.invalidateQueries({
              queryKey: accountQueries.selfProfile(userId).queryKey,
            })
          }
        }, 2000)
      }
      args.onSuccess?.()
    },
    onError: (err) => {
      haptic.error()
      const message =
        (err as { message?: string } | null)?.message ?? 'Please try again in a moment.'
      toast.show("Couldn't save changes", { message, preset: 'error' })
    },
  })
}

// Companion mutation for `delete_avatar` — same reconcile shape, same
// delayed re-fetch, but the response is the post-delete `Account` (with the
// default avatar) so the avatar-specific re-fetch is unnecessary. Wired in
// the inline Edit Profile screen's "Delete photo" path.
export function useDeleteAvatarMutation(args: Args = {}) {
  const toast = useToastController()
  // Same null-tolerance reasoning as `useUpdateCredentialsMutation` above.
  const userId = AccountStore.activeAccount()?.userId ?? ''

  return useCacheMutation<any, void>({
    mutationFn: async () => {
      const { deleteAvatar } = await import('src/lib/api')
      return deleteAvatar()
    },
    snapshotKeys: () =>
      listScopeKeys(
        accountQueries.self().queryKey,
        accountQueries.selfProfile(userId).queryKey
      ),
    reconcile: (qc, server) => reconcileBothKeys(qc, server, userId),
    onSuccess: (_data, _vars, qc) => {
      haptic.success()
      // Pixelfed's delete path returns immediately but the CDN URL flips
      // a beat later — re-fetch to make sure the UI ends on truth.
      setTimeout(() => {
        qc.invalidateQueries({ queryKey: accountQueries.self().queryKey })
        if (userId) {
          qc.invalidateQueries({
            queryKey: accountQueries.selfProfile(userId).queryKey,
          })
        }
      }, 2000)
      args.onSuccess?.()
    },
    onError: () => {
      haptic.error()
      toast.show("Couldn't delete photo", {
        message: 'Please try again in a moment.',
        preset: 'error',
      })
    },
  })
}
