import { useQueryClient } from '@tanstack/react-query'
import { type RsvpAction, rsvpEvent } from 'src/lib/api'
import { ApiError } from 'src/lib/api-context'
import type {
  PfEventJoinMode,
  PfEventParticipationStatus,
  Status,
} from 'src/lib/api-types'
import { isEventPast } from 'src/lib/events'
import { queryKeys } from 'src/lib/queries/keys'
import { emit } from 'src/lib/tapestry/emit'
import { AccountStore } from 'src/state/accountStore'
import { appKeys, appStorage } from 'src/state/storage'
import { useToastController } from 'tamagui'
import { postScopeKeys, useCacheMutation } from './cacheMutation'
import { updatePostInCache } from './updatePostInCache'

type RsvpMutateType = {
  id: string
  action: RsvpAction
  profileId?: string
}

function optimisticStatus(
  action: RsvpAction,
  joinMode: PfEventJoinMode | null
): PfEventParticipationStatus | null {
  if (action === 'withdraw') return null
  if (action === 'interested') return 'interested'
  if (action === 'declined') return 'declined'
  return joinMode === 'restricted' ? 'going_pending' : 'going'
}

export function useRsvpMutation({
  onSuccess,
  profileId,
}: {
  onSuccess?: (data: any, variables: RsvpMutateType) => void
  profileId?: string
} = {}) {
  const queryClient = useQueryClient()
  const toast = useToastController()

  const profileKey = (vars: RsvpMutateType) => {
    const uid = vars.profileId ?? profileId
    return uid ? [queryKeys.posts.byUser(uid)] : []
  }

  const { mutate, isPending } = useCacheMutation<Status, RsvpMutateType>({
    label: 'Updating RSVP…',
    mutationFn: (vars) => rsvpEvent(vars.id, vars.action),

    snapshotKeys: (vars) => postScopeKeys(vars.id, profileKey(vars)),

    applyOptimistic: (qc, vars) => {
      updatePostInCache(
        qc,
        vars.id,
        (post) => {
          if (!post.pf_event) return post
          const nextStatus = optimisticStatus(vars.action, post.pf_event.join_mode)
          return {
            ...post,
            pf_event: {
              ...post.pf_event,
              my_participation: {
                status: nextStatus,
                updated_at: post.pf_event.my_participation?.updated_at ?? null,
                cancellation_token:
                  post.pf_event.my_participation?.cancellation_token ?? null,
              },
            },
          }
        },
        profileKey(vars)
      )
    },

    reconcile: (qc, data, vars) => {
      updatePostInCache(qc, data.id, () => data, profileKey(vars))
    },

    onError: (err) => {
      if (err instanceof ApiError && err.status === 404) {
        // Per-instance escape hatch: nodeinfo may advertise the flag while
        // the RSVP endpoint isn't actually wired. Flip it off here so
        // `useEventRsvpAvailable` stops rendering RSVP on this instance.
        const instance = AccountStore.activeAccount()?.instance
        if (instance) appStorage.set(appKeys.rsvpMissing(instance), true)
        toast.show("RSVPs aren't enabled on this instance", { preset: 'error' })
        return
      }
      if (err instanceof ApiError && err.status === 422 && err.code === 'event_ended') {
        toast.show('Event has ended', { preset: 'error' })
      }
    },

    onSuccess: (data, vars) => {
      emit('V')
      onSuccess?.(data, vars)
    },
  })

  const handleRsvp = async (id: string, action: RsvpAction, statusProfileId?: string) => {
    // Belt-and-braces past-event gate. The UI disables the button when the
    // event is past, but imperative callers (deep links, test harness) can
    // still reach here with a stale cached state.
    const cached = queryClient.getQueryData<Status>(queryKeys.posts.byId(id))
    if (cached?.pf_event && isEventPast(cached.pf_event)) {
      toast.show('Event has ended', { preset: 'error' })
      return
    }
    try {
      await mutate({
        id,
        action,
        profileId: statusProfileId !== undefined ? statusProfileId : profileId,
      })
    } catch (_error) {}
  }

  return { handleRsvp, isRsvpPending: isPending }
}
