import { useMutation, useQueryClient } from '@tanstack/react-query'
import { photos as photosApi } from '../services/api'
import { toast } from '../components/ToastContainer'
import { formatApiError } from '../lib/apiError'
import { LIBRARY_STATS_QUERY_KEY } from './useLibraryStatsQuery'
import type { Photo } from '../types/photo'

/**
 * Centralised bulk-mutation hook used by both the RightSidebar multi-
 * select panel and the keyboard shortcut layer. Before this existed the
 * two paths each declared their own `bulkRatingMutation` /
 * `bulkColorMutation` pair — identical signatures, slightly different
 * optimistic behaviour, easy to drift.
 *
 * Each mutation:
 *  - Applies an optimistic patch to every cached photo list AND the
 *    per-photo cache so rating stars / color swatches flip instantly.
 *  - Rolls back the patch on error and surfaces a toast.
 *  - Invalidates the photo/library queries on success so server-side
 *    derived fields (needs_review, date_warning, etc.) reconcile.
 *
 * Discard lives elsewhere — its two call sites have deliberately
 * different semantics (keep-in-place for the X hotkey; strip-from-
 * timeline for the sidebar bulk button) so they don't belong here.
 */
export function useBulkPhotoMutations() {
  const queryClient = useQueryClient()

  const invalidatePhotoQueries = () => {
    queryClient.invalidateQueries({ queryKey: ['photo'] })
    queryClient.invalidateQueries({ queryKey: ['photos'] })
    queryClient.invalidateQueries({ queryKey: LIBRARY_STATS_QUERY_KEY })
  }

  // Snapshot enough of the current cache to roll back a failed mutation.
  // We only track the photos actually being patched so the snapshot
  // stays O(selection), not O(library).
  const snapshotPhotos = (ids: string[]): Map<string, Partial<Photo>> => {
    const snap = new Map<string, Partial<Photo>>()
    const want = new Set(ids)
    const lists = queryClient.getQueriesData<Photo[]>({ queryKey: ['photos'] })
    for (const [, list] of lists) {
      if (!list) continue
      for (const p of list) if (want.has(p.id)) snap.set(p.id, { ...p })
    }
    for (const id of ids) {
      if (snap.has(id)) continue
      const single = queryClient.getQueryData<Photo>(['photo', id])
      if (single) snap.set(id, { ...single })
    }
    return snap
  }

  // Apply a partial patch to every matching photo in every cached list,
  // plus the per-photo cache. Used as the optimistic-update primitive.
  const patchPhotos = (ids: string[], patch: Partial<Photo>) => {
    const want = new Set(ids)
    queryClient.setQueriesData<Photo[]>({ queryKey: ['photos'] }, (prev) =>
      prev ? prev.map((p) => (want.has(p.id) ? { ...p, ...patch } : p)) : prev,
    )
    for (const id of ids) {
      const cur = queryClient.getQueryData<Photo>(['photo', id])
      if (cur) queryClient.setQueryData<Photo>(['photo', id], { ...cur, ...patch })
    }
  }

  const restoreFromSnapshot = (snap: Map<string, Partial<Photo>>) => {
    const ids = Array.from(snap.keys())
    const want = new Set(ids)
    queryClient.setQueriesData<Photo[]>({ queryKey: ['photos'] }, (prev) =>
      prev
        ? prev.map((p) =>
            want.has(p.id) ? ({ ...p, ...snap.get(p.id) } as Photo) : p,
          )
        : prev,
    )
    for (const id of ids) {
      const cur = queryClient.getQueryData<Photo>(['photo', id])
      const orig = snap.get(id)
      if (cur && orig) {
        queryClient.setQueryData<Photo>(['photo', id], { ...cur, ...orig })
      }
    }
  }

  const bulkRating = useMutation({
    mutationFn: ({ ids, rating }: { ids: string[]; rating: number }) =>
      photosApi.bulkSetRating(ids, rating),
    onMutate: ({ ids, rating }) => {
      const snapshot = snapshotPhotos(ids)
      patchPhotos(ids, { rating })
      return { snapshot }
    },
    onError: (e, _vars, ctx) => {
      if (ctx?.snapshot) restoreFromSnapshot(ctx.snapshot)
      toast.error('Rating failed', formatApiError(e))
    },
    onSuccess: invalidatePhotoQueries,
  })

  const bulkColor = useMutation({
    mutationFn: ({ ids, color }: { ids: string[]; color: string | null }) =>
      photosApi.bulkSetColor(ids, color),
    onMutate: ({ ids, color }) => {
      const snapshot = snapshotPhotos(ids)
      patchPhotos(ids, { color_label: color })
      return { snapshot }
    },
    onError: (e, _vars, ctx) => {
      if (ctx?.snapshot) restoreFromSnapshot(ctx.snapshot)
      toast.error('Color label failed', formatApiError(e))
    },
    onSuccess: invalidatePhotoQueries,
  })

  return { bulkRating, bulkColor, invalidatePhotoQueries }
}
