import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useState, useEffect } from 'react'
import { Trash2, Check, UserPlus, CheckSquare, Square, Play, ChevronLeft, ChevronRight, RefreshCw, ClipboardList, CalendarClock, ImagePlus } from 'lucide-react'
import { useBreadcrumb } from '../hooks/useBreadcrumb'
import { breadcrumbConfig } from '../config/breadcrumbConfig'
import { api, wsClient, type ReviewFile } from '../lib/api'
import { formatBytes, formatPlatformName, isVideoFile } from '../lib/utils'
import { notificationManager } from '../lib/notificationManager'
import { invalidateAllFileCaches, optimisticRemoveFromReviewQueue } from '../lib/cacheInvalidation'
import EnhancedLightbox from '../components/EnhancedLightbox'
import { BatchProgressModal, BatchProgressItem } from '../components/BatchProgressModal'
import ThrottledImage from '../components/ThrottledImage'
import { FilterBar } from '../components/FilterPopover'
import { CopyToGalleryModal } from '../components/private-gallery/CopyToGalleryModal'
import { useEnabledFeatures } from '../hooks/useEnabledFeatures'

// Get thumbnail URL - use stored YouTube thumbnail if available
function getReviewThumbnailUrl(file: ReviewFile): string {
  if (file.platform === 'youtube' && file.video_id) {
    return `/api/video/thumbnail/${file.platform}/${file.video_id}?source=downloads`
  }
  return api.getReviewThumbnailUrl(file.file_path, file.file_token)
}

export default function Review() {
  const queryClient = useQueryClient()
  const { setBreadcrumbs } = useBreadcrumb(breadcrumbConfig['/review'])
  const { isFeatureEnabled } = useEnabledFeatures()
  const [selectedImage, setSelectedImage] = useState<ReviewFile | null>(null)
  const [page, setPage] = useState(0)
  const [showKeepModal, setShowKeepModal] = useState(false)
  const [showAddModal, setShowAddModal] = useState(false)
  const [addPersonName, setAddPersonName] = useState('')
  const [actioningFile, setActioningFile] = useState<string | null>(null)
  const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set())
  const [selectedPaths, setSelectedPaths] = useState<Map<string, string>>(new Map())
  const [selectMode, setSelectMode] = useState(false)
  const [platformFilter, setPlatformFilter] = useState<string>('')
  const [sourceFilter, setSourceFilter] = useState<string>('')
  const [typeFilter, setTypeFilter] = useState<'all' | 'image' | 'video'>('all')
  const [searchQuery, setSearchQuery] = useState<string>('')
  const [showProgressModal, setShowProgressModal] = useState(false)
  const [progressFiles, setProgressFiles] = useState<BatchProgressItem[]>([])
  const [progressTitle, setProgressTitle] = useState('Processing Files')
  const [isRescanning, setIsRescanning] = useState(false)
  const [rescanProgress, setRescanProgress] = useState<{ current: number; total: number } | null>(null)

  // Date edit modal state
  const [showDateModal, setShowDateModal] = useState(false)
  const [editingItemId, setEditingItemId] = useState<number | null>(null)
  const [newDate, setNewDate] = useState('')
  const [updateFileTimestamps, setUpdateFileTimestamps] = useState(true)

  // Copy to Private Gallery modal state
  const [showCopyToGalleryModal, setShowCopyToGalleryModal] = useState(false)

  // Advanced search filters
  const [dateFrom, setDateFrom] = useState<string>('')
  const [dateTo, setDateTo] = useState<string>('')
  const [sizeMin, setSizeMin] = useState<string>('')
  const [sizeMax, setSizeMax] = useState<string>('')
  const [sortBy, setSortBy] = useState<string>('added_date')
  const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc')

  const limit = 50

  const { data: reviewData, isLoading } = useQuery({
    queryKey: ['review-queue', page, platformFilter, sourceFilter, typeFilter, sortBy, sortOrder, dateFrom, dateTo, sizeMin, sizeMax, searchQuery],
    queryFn: () =>
      api.getReviewQueue({
        limit,
        offset: page * limit,
        platform: platformFilter || undefined,
        source: sourceFilter || undefined,
        media_type: typeFilter,
        sort_by: sortBy,
        sort_order: sortOrder,
        date_from: dateFrom || undefined,
        date_to: dateTo || undefined,
        size_min: sizeMin ? parseInt(sizeMin) : undefined,
        size_max: sizeMax ? parseInt(sizeMax) : undefined,
        search: searchQuery || undefined,
      }),
  })

  const { data: filters } = useQuery({
    queryKey: ['review-filters', platformFilter],
    queryFn: () => api.getReviewFilters(platformFilter || undefined),
  })

  // Clear source filter when platform changes and source is not available
  useEffect(() => {
    if (filters && sourceFilter && !filters.sources.includes(sourceFilter)) {
      setSourceFilter('')
    }
  }, [filters, sourceFilter])

  // Update breadcrumb when filter changes
  useEffect(() => {
    const typeLabel = typeFilter === 'all' ? '' : typeFilter === 'image' ? 'Images' : 'Videos'
    if (typeLabel) {
      setBreadcrumbs([
        { label: 'Home', path: '/' },
        { label: 'Review', path: '/review' },
        { label: typeLabel }
      ])
    } else {
      setBreadcrumbs(breadcrumbConfig['/review'])
    }
  }, [typeFilter, setBreadcrumbs])

  // Helper function to advance to next/previous item after delete/keep/add reference
  const advanceToNextItem = () => {
    if (!selectedImage) return

    const currentIndex = filteredFiles.findIndex(f => f.file_path === selectedImage.file_path)
    if (currentIndex === -1) {
      setSelectedImage(null)
      return
    }

    // Try next item first
    if (currentIndex + 1 < filteredFiles.length) {
      setSelectedImage(filteredFiles[currentIndex + 1])
    }
    // If no next item, try previous
    else if (currentIndex - 1 >= 0) {
      setSelectedImage(filteredFiles[currentIndex - 1])
    }
    // If no items left, close lightbox
    else {
      setSelectedImage(null)
    }
  }

  // Listen for face recognition completion via websocket
  useEffect(() => {
    const unsubscribe = wsClient.on('face_recognition_completed', (data: { success: boolean; filename: string; person_name?: string; error?: string; file_path?: string }) => {
      // Show toast notification
      if (data.success) {
        notificationManager.faceReferenceAdded(data.person_name || 'Unknown', data.filename)
      } else {
        notificationManager.faceReferenceError(data.error || `Failed to process ${data.filename}`)
      }

      // Update progress for this file (for progress modal if open)
      setProgressFiles(prev => prev.map(f =>
        f.filename === data.filename || f.id === data.file_path
          ? { ...f, status: data.success ? 'success' : 'error', error: data.error }
          : f
      ))

      // Refresh all caches to reflect the file move
      if (data.success) {
        invalidateAllFileCaches(queryClient)
      }
    })

    return unsubscribe
  }, [queryClient])

  // Listen for batch move progress via websocket
  useEffect(() => {
    const unsubscribe = wsClient.on('batch_move_progress', (data: { success: boolean; filename: string; file_path?: string; error?: string }) => {
      // Update progress for this file
      setProgressFiles(prev => prev.map(f =>
        f.filename === data.filename || f.id === data.file_path
          ? { ...f, status: data.success ? 'success' : 'error', error: data.error }
          : f
      ))
    })

    return unsubscribe
  }, [])

  // Listen for rescan progress via websocket
  useEffect(() => {
    const unsubscribeStarted = wsClient.on('review_rescan_started', (data: { total_files: number }) => {
      setIsRescanning(true)
      setRescanProgress({ current: 0, total: data.total_files })
      notificationManager.rescanStarted(data.total_files)
    })

    const unsubscribeProgress = wsClient.on('review_rescan_progress', (data: { current: number; total: number }) => {
      setRescanProgress({ current: data.current, total: data.total })
    })

    const unsubscribeComplete = wsClient.on('review_rescan_complete', (data: { stats: { updated: number; errors: number } }) => {
      setIsRescanning(false)
      setRescanProgress(null)
      invalidateAllFileCaches(queryClient)
      notificationManager.rescanCompleted(data.stats.updated)
    })

    const unsubscribeError = wsClient.on('review_rescan_error', (data: { error: string }) => {
      setIsRescanning(false)
      setRescanProgress(null)
      notificationManager.apiError('Rescan Failed', data.error)
    })

    return () => {
      unsubscribeStarted()
      unsubscribeProgress()
      unsubscribeComplete()
      unsubscribeError()
    }
  }, [queryClient])

  // Poll for rescan status as fallback when WebSocket isn't working
  useEffect(() => {
    if (!isRescanning) return

    const pollInterval = setInterval(async () => {
      try {
        const status = await api.reviewRescanStatus()
        if (status.running) {
          setRescanProgress({ current: status.progress.current, total: status.progress.total })
        } else {
          // Scan finished
          setIsRescanning(false)
          setRescanProgress(null)
          if (status.progress.complete) {
            invalidateAllFileCaches(queryClient)
            const count = typeof status.progress.stats?.updated === 'number' ? status.progress.stats.updated : 0
            notificationManager.rescanCompleted(count)
          } else if (status.progress.error) {
            notificationManager.apiError('Rescan Failed', status.progress.error, 'Rescan failed')
          }
          clearInterval(pollInterval)
        }
      } catch (error) {
        // Ignore polling errors
      }
    }, 1000)

    return () => clearInterval(pollInterval)
  }, [isRescanning, queryClient])

  // Check for ongoing rescan on page load
  useEffect(() => {
    const checkRescanStatus = async () => {
      try {
        const status = await api.reviewRescanStatus()
        if (status.running) {
          setIsRescanning(true)
          setRescanProgress({ current: status.progress.current, total: status.progress.total })
        }
      } catch (error) {
        // Ignore
      }
    }
    checkRescanStatus()
  }, [])

  const keepMutation = useMutation({
    mutationFn: ({ filePath, destination }: { filePath: string; destination: string }) =>
      api.reviewKeep(filePath, destination),
    onSuccess: (_data, variables) => {
      // Advance to next item before invalidating queries
      advanceToNextItem()
      optimisticRemoveFromReviewQueue(queryClient, new Set([variables.filePath]))
      invalidateAllFileCaches(queryClient)
      setShowKeepModal(false)
      setActioningFile(null)
      notificationManager.kept('Image')
      // Trigger Immich scan (don't await - fire and forget)
      api.triggerImmichScan().catch(() => {})
    },
    onError: (err: unknown) => {
      setActioningFile(null)
      notificationManager.moveError('image', err)
    },
  })

  const deleteMutation = useMutation({
    mutationFn: (filePath: string) => api.reviewDelete(filePath),
    onSuccess: (_data, variables) => {
      // Advance to next item before invalidating queries
      advanceToNextItem()
      optimisticRemoveFromReviewQueue(queryClient, new Set([variables]))
      invalidateAllFileCaches(queryClient)
      setActioningFile(null)
      notificationManager.deleted('Image', 'Image removed from review queue')
      // Trigger Immich scan (don't await - fire and forget)
      api.triggerImmichScan().catch(() => {})
    },
    onError: (err: unknown) => {
      setActioningFile(null)
      notificationManager.deleteError('image', err)
    },
  })

  const addReferenceMutation = useMutation({
    mutationFn: ({ filePath, personName, destination }: { filePath: string; personName: string; destination: string }) =>
      api.reviewAddReference(filePath, personName, destination),
    onSuccess: (data, variables) => {
      // Advance to next item before invalidating queries
      advanceToNextItem()
      optimisticRemoveFromReviewQueue(queryClient, new Set([variables.filePath]))
      queryClient.invalidateQueries({ queryKey: ['review-queue'] })
      setShowAddModal(false)
      setActioningFile(null)
      // File moved successfully, face recognition processing in background
      notificationManager.processing(`File moved to destination. Analyzing face for ${data.person_name}...`)
      // Trigger Immich scan (don't await - fire and forget)
      api.triggerImmichScan().catch(() => {})
      // WebSocket will notify when face recognition completes
    },
    onError: (err: unknown) => {
      setActioningFile(null)
      notificationManager.faceReferenceError(err)
    },
  })

  // Listen for face reference completion via WebSocket
  useEffect(() => {
    const unsubscribe = wsClient.on('face_reference_added', (data: { success: boolean; person_name?: string; error?: string }) => {
      if (data.success) {
        notificationManager.faceReferenceAdded(data.person_name || 'Unknown')
        invalidateAllFileCaches(queryClient)
      } else {
        notificationManager.faceReferenceError(data.error)
      }
    })

    return unsubscribe
  }, [queryClient])

  const addFaceReferenceMutation = useMutation({
    mutationFn: (file_path: string) => api.addFaceReference(file_path, undefined, true), // background = true
    onSuccess: (data) => {
      setSelectedImage(null) // Close lightbox immediately
      notificationManager.processing(data.message)
    },
    onError: (err: unknown) => {
      notificationManager.faceReferenceError(err)
    },
  })

  const batchDeleteMutation = useMutation({
    mutationFn: async (filePaths: string[]) => {
      // Initialize progress with all files as pending
      const progressItems: BatchProgressItem[] = filePaths.map(fp => ({
        id: fp,
        filename: fp.split('/').pop() || fp,
        status: 'pending' as const
      }))
      setProgressFiles(progressItems)
      setProgressTitle('Deleting Files')
      setShowProgressModal(true)

      let succeeded = 0
      let failed = 0

      // Process files one by one for real-time progress
      for (let i = 0; i < filePaths.length; i++) {
        const filePath = filePaths[i]

        // Update progress to 'processing'
        setProgressFiles(prev => prev.map(f =>
          f.id === filePath ? { ...f, status: 'processing' } : f
        ))

        try {
          await api.reviewDelete(filePath)
          succeeded++
          setProgressFiles(prev => prev.map(f =>
            f.id === filePath ? { ...f, status: 'success' } : f
          ))
        } catch (error) {
          failed++
          const errorMsg = (error as Error)?.message || 'Failed to delete'
          setProgressFiles(prev => prev.map(f =>
            f.id === filePath ? { ...f, status: 'error', error: errorMsg } : f
          ))
        }
      }

      return { succeeded, failed, total: filePaths.length }
    },
    onSuccess: (data, variables) => {
      optimisticRemoveFromReviewQueue(queryClient, new Set(variables))
      invalidateAllFileCaches(queryClient)
      setSelectedItems(new Set())
      setSelectedPaths(new Map())
      setSelectMode(false)

      // Keep modal open briefly to show completion
      setTimeout(() => {
        setShowProgressModal(false)
        notificationManager.batchSuccess('Delete', data.succeeded, 'item')
      }, 1500)

      // Trigger Immich scan (don't await - fire and forget)
      api.triggerImmichScan().catch(() => {})
    },
    onError: (err: unknown) => {
      setShowProgressModal(false)
      notificationManager.batchError('Delete', err)
    },
  })

  const batchKeepMutation = useMutation({
    mutationFn: async ({ filePaths }: { filePaths: string[] }) => {
      let succeeded = 0
      let failed = 0

      // Process files one by one
      for (let i = 0; i < filePaths.length; i++) {
        const filePath = filePaths[i]

        // Update progress to 'processing'
        setProgressFiles(prev => prev.map(f =>
          f.id === filePath ? { ...f, status: 'processing' } : f
        ))

        try {
          await api.reviewKeep(filePath, '')
          succeeded++
          // Update to success immediately since we're processing sequentially
          setProgressFiles(prev => prev.map(f =>
            f.id === filePath ? { ...f, status: 'success' } : f
          ))
        } catch (error) {
          failed++
          // Update progress to 'error' immediately if API call fails
          const errorMsg = (error as Error)?.message || 'Failed to process'
          setProgressFiles(prev => prev.map(f =>
            f.id === filePath ? { ...f, status: 'error', error: errorMsg } : f
          ))
        }
      }

      return { succeeded, failed }
    },
    onSuccess: (data, variables) => {
      optimisticRemoveFromReviewQueue(queryClient, new Set(variables.filePaths))
      invalidateAllFileCaches(queryClient)
      setSelectedItems(new Set())
      setSelectedPaths(new Map())
      setSelectMode(false)
      setShowKeepModal(false)

      // Keep progress modal open for 2 seconds before closing
      setTimeout(() => {
        setShowProgressModal(false)
        notificationManager.batchSuccess('Keep', data.succeeded, 'file')
      }, 2000)
      // Trigger Immich scan (don't await - fire and forget)
      api.triggerImmichScan().catch(() => {})
    },
    onError: (err: unknown) => {
      setShowProgressModal(false)
      notificationManager.batchError('Keep', err)
    },
  })

  const batchAddReferenceMutation = useMutation({
    mutationFn: async ({ filePaths, personName }: { filePaths: string[]; personName: string }) => {
      let succeeded = 0
      let failed = 0

      // Process files one by one
      for (let i = 0; i < filePaths.length; i++) {
        const filePath = filePaths[i]

        // Update progress to 'processing'
        setProgressFiles(prev => prev.map(f =>
          f.id === filePath ? { ...f, status: 'processing' } : f
        ))

        try {
          await api.reviewAddReference(filePath, personName, '')
          succeeded++
          // Update to success immediately
          setProgressFiles(prev => prev.map(f =>
            f.id === filePath ? { ...f, status: 'success' } : f
          ))
        } catch (error) {
          failed++
          // Update progress to 'error'
          const errorMsg = (error as Error)?.message || 'Failed to process'
          setProgressFiles(prev => prev.map(f =>
            f.id === filePath ? { ...f, status: 'error', error: errorMsg } : f
          ))
        }
      }

      return { succeeded, failed }
    },
    onSuccess: (data, variables) => {
      optimisticRemoveFromReviewQueue(queryClient, new Set(variables.filePaths))
      invalidateAllFileCaches(queryClient)
      setSelectedItems(new Set())
      setSelectedPaths(new Map())
      setSelectMode(false)

      // Keep progress modal open for 2 seconds before closing
      setTimeout(() => {
        setShowProgressModal(false)
        notificationManager.batchSuccess('Add Reference', data.succeeded, 'file')
      }, 2000)
      // Trigger Immich scan (don't await - fire and forget)
      api.triggerImmichScan().catch(() => {})
    },
    onError: (err: unknown) => {
      setShowProgressModal(false)
      notificationManager.batchError('Add Reference', err)
    },
  })

  const updateDateMutation = useMutation({
    mutationFn: ({ ids, newDate, updateFile }: { ids: number[]; newDate: string; updateFile: boolean }) =>
      api.updateMediaDate(ids, newDate, updateFile, 'post_date'),
    onSuccess: (data) => {
      invalidateAllFileCaches(queryClient)
      setShowDateModal(false)
      setNewDate('')
      setEditingItemId(null)
      notificationManager.dateUpdated(`${data.success_count} file${data.success_count !== 1 ? 's' : ''}`)
    },
    onError: (err: unknown) => {
      notificationManager.dateUpdateError(err)
    },
  })

  // All filtering and sorting is now server-side
  const filteredFiles = reviewData?.files || []

  const totalPages = Math.ceil((reviewData?.total || 0) / limit)

  const toggleSelect = (filePath: string) => {
    const newSelected = new Set(selectedItems)
    const newPaths = new Map(selectedPaths)
    if (newSelected.has(filePath)) {
      newSelected.delete(filePath)
      newPaths.delete(filePath)
    } else {
      newSelected.add(filePath)
      newPaths.set(filePath, filePath)
    }
    setSelectedItems(newSelected)
    setSelectedPaths(newPaths)
  }

  const selectAll = () => {
    if (selectedItems.size === filteredFiles.length) {
      setSelectedItems(new Set())
      setSelectedPaths(new Map())
    } else {
      setSelectedItems(new Set(filteredFiles.map(f => f.file_path)))
      setSelectedPaths(new Map(filteredFiles.map(f => [f.file_path, f.file_path])))
    }
  }

  const handleBatchDelete = () => {
    if (selectedItems.size === 0) return
    if (confirm(`Delete ${selectedItems.size} selected items?`)) {
      batchDeleteMutation.mutate(Array.from(selectedItems))
    }
  }

  const handleBatchKeep = () => {
    if (selectedItems.size === 0) return
    setShowKeepModal(true)
  }

  const handleDeleteAll = async () => {
    const total = reviewData?.total || 0
    if (total === 0) return

    if (confirm(`Delete ALL ${total} items in the review queue? This cannot be undone.`)) {
      try {
        // Fetch all file paths from the review queue
        const response = await api.getAllReviewFilePaths()
        batchDeleteMutation.mutate(response.file_paths)
      } catch (err) {
        console.error('Failed to fetch all review file paths:', err)
        notificationManager.apiError('Error', err, 'Failed to fetch all review files')
      }
    }
  }

  const handleKeepAll = async () => {
    const total = reviewData?.total || 0
    if (total === 0) return

    try {
      // Fetch all file paths from the review queue
      const response = await api.getAllReviewFilePaths()
      setSelectedItems(new Set(response.file_paths))
      setShowKeepModal(true)
    } catch (err) {
      console.error('Failed to fetch all review file paths:', err)
      notificationManager.apiError('Error', err, 'Failed to fetch all review files')
    }
  }

  const confirmBatchKeep = () => {
    if (selectedItems.size === 0) return

    // Close the keep modal
    setShowKeepModal(false)

    // Get all file paths
    const filePaths = Array.from(selectedItems)

    // Initialize progress tracking
    const progressItems: BatchProgressItem[] = filePaths.map(fp => ({
      id: fp,
      filename: fp.split('/').pop() || fp,
      status: 'pending' as const
    }))

    setProgressFiles(progressItems)
    setProgressTitle('Moving Files to Media Library')
    setShowProgressModal(true)

    // Start the batch operation
    batchKeepMutation.mutate({
      filePaths
    })
  }

  const handleBatchAddReference = () => {
    if (selectedItems.size === 0) return
    setShowAddModal(true)
  }

  const confirmBatchAddReference = () => {
    if (!addPersonName.trim() || selectedItems.size === 0) return

    // Close the add modal
    setShowAddModal(false)

    // Get all file paths
    const filePaths = Array.from(selectedItems)

    // Initialize progress tracking
    const progressItems: BatchProgressItem[] = filePaths.map(fp => ({
      id: fp,
      filename: fp.split('/').pop() || fp,
      status: 'pending' as const
    }))
    setProgressFiles(progressItems)
    setProgressTitle(`Adding Face References for "${addPersonName}"`)
    setShowProgressModal(true)

    // Start processing
    batchAddReferenceMutation.mutate({
      filePaths,
      personName: addPersonName
    })
  }

  const openLightbox = (file: ReviewFile) => {
    setSelectedImage(file)
  }

  // Helper to get current index for lightbox
  const getCurrentLightboxIndex = () => {
    if (!selectedImage) return -1
    return filteredFiles.findIndex((f) => f.file_path === selectedImage.file_path)
  }

  const handleKeep = (file: ReviewFile) => {
    setActioningFile(file.file_path)
    setShowKeepModal(true)
  }

  const confirmKeep = () => {
    // Batch operation
    if (selectedItems.size > 0) {
      confirmBatchKeep()
    }
    // Single file operation
    else if (actioningFile) {
      keepMutation.mutate({ filePath: actioningFile, destination: '' })
    }
  }

  const handleDelete = (file: ReviewFile) => {
    if (confirm(`Delete ${file.filename}?`)) {
      setActioningFile(file.file_path)
      deleteMutation.mutate(file.file_path)
    }
  }

  const handleAddReference = (file: ReviewFile) => {
    setActioningFile(file.file_path)
    setShowAddModal(true)
  }

  // Date editing handlers
  const handleSingleChangeDate = (media: ReviewFile) => {
    setEditingItemId(media.id ?? null)
    // Pre-fill with current post_date if available
    if (media.post_date) {
      const date = new Date(media.post_date)
      const localDateTime = new Date(date.getTime() - date.getTimezoneOffset() * 60000)
        .toISOString()
        .slice(0, 16)
      setNewDate(localDateTime)
    } else {
      setNewDate('')
    }
    setShowDateModal(true)
  }

  const confirmDateChange = () => {
    if (!newDate || !editingItemId) return
    const isoDate = new Date(newDate).toISOString()
    updateDateMutation.mutate({ ids: [editingItemId], newDate: isoDate, updateFile: updateFileTimestamps })
  }

  const confirmAddReference = () => {
    if (!addPersonName.trim()) return

    // Batch operation
    if (selectedItems.size > 0) {
      confirmBatchAddReference()
    }
    // Single file operation
    else if (actioningFile) {
      addReferenceMutation.mutate({
        filePath: actioningFile,
        personName: addPersonName,
        destination: '',
      })
    }
  }

  return (
    <div className="space-y-6">
      {/* Header */}
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-2xl sm:text-3xl font-bold flex items-center gap-2 sm:gap-3">
            <ClipboardList className="w-8 h-8 text-yellow-500" />
            Review Queue
          </h1>
          <p className="text-slate-600 dark:text-slate-400 mt-1">
            Images that didn't match face recognition - review and take action
          </p>
        </div>
        <div className="flex flex-wrap gap-3">
          <button
            onClick={() => setSelectMode(!selectMode)}
            className="px-4 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 min-h-[44px] transition-colors"
          >
            {selectMode ? 'Cancel Selection' : 'Select Multiple'}
          </button>
          <button
            onClick={async () => {
              try {
                await api.reviewRescan()
              } catch (err) {
                notificationManager.apiError('Rescan Failed', err, 'Failed to start rescan')
              }
            }}
            disabled={(reviewData?.total || 0) === 0 || isRescanning}
            className="px-4 py-2.5 bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2 min-h-[44px] transition-colors"
          >
            <RefreshCw className={`w-4 h-4 ${isRescanning ? 'animate-spin' : ''}`} />
            {isRescanning && rescanProgress
              ? `Scanning ${rescanProgress.current}/${rescanProgress.total}`
              : 'Rescan Faces'}
          </button>
          <button
            onClick={handleKeepAll}
            disabled={(reviewData?.total || 0) === 0 || batchKeepMutation.isPending}
            className="px-4 py-2.5 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed min-h-[44px] transition-colors"
          >
            Keep All ({reviewData?.total || 0})
          </button>
          <button
            onClick={handleDeleteAll}
            disabled={(reviewData?.total || 0) === 0 || batchDeleteMutation.isPending}
            className="px-4 py-2.5 bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed min-h-[44px] transition-colors"
          >
            Delete All ({reviewData?.total || 0})
          </button>
        </div>
      </div>

      {/* Selection Bar */}
      {selectMode && filteredFiles.length > 0 && (
        <div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
          <div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
            <div className="flex flex-wrap items-center gap-3">
              <button
                onClick={selectAll}
                className="flex items-center space-x-2 px-3 py-2 bg-white dark:bg-slate-800 border border-slate-300 dark:border-slate-600 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-700 text-sm min-h-[44px] transition-colors"
              >
                {selectedItems.size === filteredFiles.length ? (
                  <CheckSquare className="w-5 h-5" />
                ) : (
                  <Square className="w-5 h-5" />
                )}
                <span>{selectedItems.size === filteredFiles.length ? 'Deselect All' : 'Select All'}</span>
              </button>
              <span className="text-sm text-blue-700 dark:text-blue-400">
                {selectedItems.size} item{selectedItems.size !== 1 ? 's' : ''} selected
              </span>
            </div>
            <div className="flex flex-wrap items-center gap-2">
              <button
                onClick={handleBatchKeep}
                disabled={selectedItems.size === 0 || batchKeepMutation.isPending}
                className="flex items-center space-x-2 px-3 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed min-h-[44px] text-sm transition-colors"
              >
                <Check className="w-5 h-5" />
                <span className="hidden sm:inline">Keep Selected</span>
                <span className="sm:hidden">Keep</span>
              </button>
              {isFeatureEnabled('/private-gallery') && (
              <button
                onClick={() => setShowCopyToGalleryModal(true)}
                disabled={selectedItems.size === 0}
                className="flex items-center space-x-2 px-3 py-2 bg-violet-600 text-white rounded-lg hover:bg-violet-700 disabled:opacity-50 disabled:cursor-not-allowed min-h-[44px] text-sm transition-colors"
              >
                <ImagePlus className="w-5 h-5" />
                <span className="hidden sm:inline">Copy to Private</span>
                <span className="sm:hidden">Private</span>
              </button>
              )}
              <button
                onClick={handleBatchAddReference}
                disabled={selectedItems.size === 0 || batchAddReferenceMutation.isPending}
                className="flex items-center space-x-2 px-3 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed min-h-[44px] text-sm transition-colors"
              >
                <UserPlus className="w-5 h-5" />
                <span className="hidden sm:inline">Add as Reference</span>
                <span className="sm:hidden">Reference</span>
              </button>
              <button
                onClick={handleBatchDelete}
                disabled={selectedItems.size === 0 || batchDeleteMutation.isPending}
                className="flex items-center space-x-2 px-3 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed min-h-[44px] text-sm transition-colors"
              >
                <Trash2 className="w-5 h-5" />
                <span className="hidden sm:inline">Delete Selected</span>
                <span className="sm:hidden">Delete</span>
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Filters */}
      <FilterBar
        searchValue={searchQuery}
        onSearchChange={(v) => { setSearchQuery(v); setPage(0) }}
        searchPlaceholder="Search filenames..."
        filterSections={[
          {
            id: 'platform',
            label: 'Platform',
            type: 'select',
            options: [{ value: '', label: 'All Platforms' }, ...(filters?.platforms || []).map(p => ({ value: p, label: formatPlatformName(p) }))],
            value: platformFilter,
            onChange: (v) => { setPlatformFilter(v as string); setPage(0) }
          },
          {
            id: 'source',
            label: 'Source',
            type: 'select',
            options: [{ value: '', label: 'All Sources' }, ...(filters?.sources || []).map(s => ({ value: s, label: s }))],
            value: sourceFilter,
            onChange: (v) => { setSourceFilter(v as string); setPage(0) }
          },
          {
            id: 'type',
            label: 'Media Type',
            type: 'select',
            options: [
              { value: 'all', label: 'All Media' },
              { value: 'image', label: 'Images Only' },
              { value: 'video', label: 'Videos Only' }
            ],
            value: typeFilter,
            onChange: (v) => { setTypeFilter(v as 'all' | 'image' | 'video'); setPage(0) }
          },
          {
            id: 'sortBy',
            label: 'Sort By',
            type: 'select',
            options: [
              { value: 'post_date', label: 'Post Date' },
              { value: 'added_date', label: 'Added Date' },
              { value: 'file_size', label: 'File Size' },
              { value: 'filename', label: 'Filename' },
              { value: 'source', label: 'Source' },
              { value: 'platform', label: 'Platform' },
              { value: 'confidence', label: 'Face Confidence' }
            ],
            value: sortBy,
            onChange: (v) => { setSortBy(v as string); setPage(0) }
          },
          {
            id: 'sortOrder',
            label: 'Sort Order',
            type: 'select',
            options: sortBy === 'confidence'
              ? [
                  { value: 'desc', label: 'Highest First' },
                  { value: 'asc', label: 'Lowest First' }
                ]
              : [
                  { value: 'desc', label: 'Newest First' },
                  { value: 'asc', label: 'Oldest First' }
                ],
            value: sortOrder,
            onChange: (v) => { setSortOrder(v as 'asc' | 'desc'); setPage(0) }
          }
        ]}
        activeFilters={[
          ...(platformFilter ? [{
            id: 'platform', label: 'Platform', value: platformFilter,
            displayValue: formatPlatformName(platformFilter),
            onRemove: () => { setPlatformFilter(''); setPage(0) }
          }] : []),
          ...(sourceFilter ? [{
            id: 'source', label: 'Source', value: sourceFilter,
            displayValue: sourceFilter,
            onRemove: () => { setSourceFilter(''); setPage(0) }
          }] : []),
          ...(typeFilter !== 'all' ? [{
            id: 'type', label: 'Type', value: typeFilter,
            displayValue: typeFilter === 'image' ? 'Images' : 'Videos',
            onRemove: () => { setTypeFilter('all'); setPage(0) }
          }] : []),
          ...(dateFrom ? [{
            id: 'dateFrom', label: 'From', value: dateFrom,
            displayValue: dateFrom,
            onRemove: () => { setDateFrom(''); setPage(0) }
          }] : []),
          ...(dateTo ? [{
            id: 'dateTo', label: 'To', value: dateTo,
            displayValue: dateTo,
            onRemove: () => { setDateTo(''); setPage(0) }
          }] : []),
          ...(sizeMin ? [{
            id: 'sizeMin', label: 'Min Size', value: sizeMin,
            displayValue: `${sizeMin} bytes`,
            onRemove: () => { setSizeMin(''); setPage(0) }
          }] : []),
          ...(sizeMax ? [{
            id: 'sizeMax', label: 'Max Size', value: sizeMax,
            displayValue: `${sizeMax} bytes`,
            onRemove: () => { setSizeMax(''); setPage(0) }
          }] : [])
        ]}
        onClearAll={() => {
          setPlatformFilter('')
          setSourceFilter('')
          setTypeFilter('all')
          setSearchQuery('')
          setDateFrom('')
          setDateTo('')
          setSizeMin('')
          setSizeMax('')
          setPage(0)
        }}
        advancedFilters={{
          dateFrom: { value: dateFrom, onChange: (v) => { setDateFrom(v); setPage(0) } },
          dateTo: { value: dateTo, onChange: (v) => { setDateTo(v); setPage(0) } },
          sizeMin: { value: sizeMin, onChange: (v) => { setSizeMin(v); setPage(0) } },
          sizeMax: { value: sizeMax, onChange: (v) => { setSizeMax(v); setPage(0) } }
        }}
        totalCount={reviewData?.total}
        countLabel="items"
      />

      {/* Stats */}
      <div className="flex items-center justify-between text-sm text-slate-600 dark:text-slate-400">
        <div>
          Showing {filteredFiles.length} of {reviewData?.total || 0} items
        </div>
        {totalPages > 1 && (
          <div>
            Page {page + 1} of {totalPages}
          </div>
        )}
      </div>

      {/* Gallery Grid */}
      {isLoading ? (
        <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
          {[...Array(20)].map((_, i) => (
            <div key={i} className="aspect-square bg-slate-200 dark:bg-slate-800 rounded-lg animate-pulse" />
          ))}
        </div>
      ) : filteredFiles.length === 0 ? (
        <div className="text-center py-20">
          <p className="text-slate-500 dark:text-slate-400 text-lg">
            {reviewData?.total === 0 ? 'No images in review queue' : 'No items match the current filter'}
          </p>
          <p className="text-slate-400 dark:text-slate-500 text-sm mt-2">
            {reviewData?.total === 0
              ? "Images that don't match face recognition will appear here"
              : 'Try changing the filter to see more items'}
          </p>
        </div>
      ) : (
        <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
          {filteredFiles.map((file) => (
            <div
              key={file.file_path}
              onClick={() => selectMode ? toggleSelect(file.file_path) : openLightbox(file)}
              className={`group relative aspect-square bg-slate-100 dark:bg-slate-800 rounded-lg overflow-hidden cursor-pointer hover:ring-2 card-lift thumbnail-zoom ${
                selectedItems.has(file.file_path)
                  ? 'ring-2 ring-blue-500'
                  : 'hover:ring-blue-500'
              }`}
            >
              {/* Thumbnail - use img for both images and videos (thumbnail endpoint returns JPEG) */}
              <ThrottledImage
                src={getReviewThumbnailUrl(file)}
                alt={file.filename}
                className="w-full h-full object-cover"
              />

              {/* Face recognition badge - show confidence for both matches and non-matches */}
              {file.face_recognition?.scanned && (
                <div className={`absolute ${selectMode ? 'top-10' : 'top-2'} left-2 px-1.5 py-0.5 text-white text-xs font-medium rounded z-10 ${
                  file.face_recognition.matched
                    ? (file.face_recognition.confidence ?? 0) >= 0.8
                      ? 'bg-green-600'
                      : (file.face_recognition.confidence ?? 0) >= 0.5
                        ? 'bg-yellow-600'
                        : 'bg-orange-600'
                    : file.face_recognition.confidence && file.face_recognition.confidence > 0
                      ? 'bg-red-600'  // Below threshold - show in red
                      : 'bg-gray-500' // No confidence data
                }`}>
                  {file.face_recognition.confidence && file.face_recognition.confidence > 0 ? (
                    <>{Math.round(file.face_recognition.confidence * 100)}%</>
                  ) : (
                    'No Match'
                  )}
                </div>
              )}

              {/* Video Icon Indicator */}
              {isVideoFile(file.filename) && (
                <div className="absolute top-2 right-2 bg-black/70 rounded-full p-1.5">
                  <Play className="w-4 h-4 text-white fill-white" />
                </div>
              )}

              {/* Select Checkbox */}
              {selectMode && (
                <div className="absolute top-2 left-2 z-20">
                  <div className={`w-6 h-6 rounded border-2 flex items-center justify-center transition-colors ${
                    selectedItems.has(file.file_path)
                      ? 'bg-blue-600 border-blue-600'
                      : 'bg-white/90 border-slate-300'
                  }`}>
                    {selectedItems.has(file.file_path) && (
                      <Check className="w-4 h-4 text-white" />
                    )}
                  </div>
                </div>
              )}

              {/* Action buttons overlay */}
              {!selectMode && (
                <div className="absolute inset-0 bg-black/60 opacity-0 group-hover:opacity-100 transition-opacity flex flex-col items-center justify-center space-y-2 p-2">
                    <button
                      onClick={(e) => {
                        e.stopPropagation()
                        handleKeep(file)
                      }}
                      disabled={actioningFile === file.file_path}
                      className="w-full px-3 py-2.5 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 flex items-center justify-center space-x-2 text-sm min-h-[44px] transition-colors"
                    >
                      <Check className="w-4 h-4" />
                      <span>Keep</span>
                    </button>
                    <button
                      onClick={(e) => {
                        e.stopPropagation()
                        handleAddReference(file)
                      }}
                      disabled={actioningFile === file.file_path}
                      className="w-full px-3 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 flex items-center justify-center space-x-2 text-sm min-h-[44px] transition-colors"
                    >
                      <UserPlus className="w-4 h-4" />
                      <span>Add Reference</span>
                    </button>
                    <button
                      onClick={(e) => {
                        e.stopPropagation()
                        handleDelete(file)
                      }}
                      disabled={actioningFile === file.file_path}
                      className="w-full px-3 py-2.5 bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50 flex items-center justify-center space-x-2 text-sm min-h-[44px] transition-colors"
                    >
                      <Trash2 className="w-4 h-4" />
                      <span>Delete</span>
                    </button>
                  </div>
                )}

              {/* File info */}
              {!selectMode && (
                <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-2 opacity-0 group-hover:opacity-100 transition-opacity">
                  <p className="text-white text-xs truncate">{file.filename}</p>
                  <p className="text-white/70 text-xs">{formatBytes(file.file_size)}</p>
                </div>
              )}
            </div>
          ))}
        </div>
      )}

      {/* Pagination */}
      {totalPages > 1 && (
        <div className="flex items-center justify-center space-x-2">
          <button
            onClick={() => setPage(Math.max(0, page - 1))}
            disabled={page === 0}
            className="p-2 rounded-lg bg-slate-200 dark:bg-slate-800 text-slate-700 dark:text-slate-300 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-slate-300 dark:hover:bg-slate-700 min-w-[44px] min-h-[44px] transition-colors"
          >
            <ChevronLeft className="w-5 h-5" />
          </button>
          <span className="text-sm text-slate-600 dark:text-slate-400">
            Page {page + 1} of {totalPages}
          </span>
          <button
            onClick={() => setPage(Math.min(totalPages - 1, page + 1))}
            disabled={page >= totalPages - 1}
            className="p-2 rounded-lg bg-slate-200 dark:bg-slate-800 text-slate-700 dark:text-slate-300 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-slate-300 dark:hover:bg-slate-700 min-w-[44px] min-h-[44px] transition-colors"
          >
            <ChevronRight className="w-5 h-5" />
          </button>
        </div>
      )}

      {/* Lightbox */}
      {selectedImage && getCurrentLightboxIndex() >= 0 && (
        <EnhancedLightbox
          items={filteredFiles}
          currentIndex={getCurrentLightboxIndex()}
          onClose={() => setSelectedImage(null)}
          onNavigate={(index) => setSelectedImage(filteredFiles[index])}
          onDelete={handleDelete}
          onEditDate={handleSingleChangeDate}
          getPreviewUrl={(item) => api.getReviewPreviewUrl(item.file_path, item.file_token)}
          getThumbnailUrl={(item: ReviewFile) => getReviewThumbnailUrl(item)}
          isVideo={(item) => isVideoFile(item.filename)}
          renderActions={(item) => (
            <>
              <button
                onClick={() => handleKeep(item)}
                disabled={actioningFile === item.file_path}
                className="px-3 py-1.5 md:px-6 md:py-3 bg-green-600 text-white text-sm md:text-base rounded-lg hover:bg-green-700 disabled:opacity-50 flex items-center space-x-1.5 md:space-x-2 transition-colors"
              >
                <Check className="w-4 h-4 md:w-5 md:h-5" />
                <span>Keep</span>
              </button>
              <button
                onClick={() => handleAddReference(item)}
                disabled={actioningFile === item.file_path}
                className="px-3 py-1.5 md:px-6 md:py-3 bg-blue-600 text-white text-sm md:text-base rounded-lg hover:bg-blue-700 disabled:opacity-50 flex items-center space-x-1.5 md:space-x-2 transition-colors"
              >
                <UserPlus className="w-4 h-4 md:w-5 md:h-5" />
                <span>Add Reference</span>
              </button>
              <button
                onClick={() => addFaceReferenceMutation.mutate(item.file_path)}
                disabled={addFaceReferenceMutation.isPending}
                className="px-3 py-1.5 md:px-6 md:py-3 bg-purple-600 text-white text-sm md:text-base rounded-lg hover:bg-purple-700 disabled:opacity-50 flex items-center space-x-1.5 md:space-x-2 transition-colors"
              >
                <UserPlus className="w-4 h-4 md:w-5 md:h-5" />
                <span>{addFaceReferenceMutation.isPending ? 'Adding...' : 'Quick Add Face'}</span>
              </button>
              <button
                onClick={() => handleDelete(item)}
                disabled={actioningFile === item.file_path}
                className="px-3 py-1.5 md:px-6 md:py-3 bg-red-600 text-white text-sm md:text-base rounded-lg hover:bg-red-700 disabled:opacity-50 flex items-center space-x-1.5 md:space-x-2 transition-colors"
              >
                <Trash2 className="w-4 h-4 md:w-5 md:h-5" />
                <span>Delete</span>
              </button>
            </>
          )}
        />
      )}

      {/* Keep Modal */}
      {showKeepModal && (
        <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
          <div className="bg-card border border-border rounded-xl shadow-2xl p-6 max-w-md w-full">
            <h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-4">
              {selectedItems.size > 0 ? `Move ${selectedItems.size} Items` : 'Move to Destination'}
            </h3>
            <p className="text-sm text-slate-600 dark:text-slate-400 mb-4">
              {selectedItems.size > 0
                ? 'Each file will be moved to its intended destination from the database.'
                : 'This file will be moved to its intended destination from the database.'}
            </p>
            <div className="flex justify-end space-x-2">
              <button
                onClick={() => {
                  setShowKeepModal(false)
                  setActioningFile(null)
                }}
                className="px-4 py-2.5 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg min-h-[44px] transition-colors"
              >
                Cancel
              </button>
              <button
                onClick={confirmKeep}
                disabled={keepMutation.isPending}
                className="px-4 py-2.5 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 min-h-[44px] transition-colors"
              >
                {keepMutation.isPending ? 'Moving...' : 'Move'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Add Reference Modal */}
      {showAddModal && (
        <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
          <div className="bg-card border border-border rounded-xl shadow-2xl p-6 max-w-md w-full">
            <h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-4">
              {selectedItems.size > 0 ? `Add ${selectedItems.size} as Face References` : 'Add as Face Reference'}
            </h3>
            <p className="text-sm text-slate-600 dark:text-slate-400 mb-4">
              {selectedItems.size > 0
                ? `This will add faces from ${selectedItems.size} image${selectedItems.size !== 1 ? 's' : ''} as references for future matching. Each file will be moved to its intended destination from the database.`
                : 'This will add the face in this image as a reference for future matching and move it to its intended destination from the database.'}
            </p>
            <div>
              <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
                Person Name
              </label>
              <input
                type="text"
                value={addPersonName}
                onChange={(e) => setAddPersonName(e.target.value)}
                placeholder="e.g. Eva Longoria"
                className="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground"
                autoFocus
              />
            </div>
            <div className="flex justify-end space-x-2 mt-6">
              <button
                onClick={() => {
                  setShowAddModal(false)
                  setActioningFile(null)
                }}
                className="px-4 py-2.5 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg min-h-[44px] transition-colors"
              >
                Cancel
              </button>
              <button
                onClick={confirmAddReference}
                disabled={!addPersonName.trim() || addReferenceMutation.isPending}
                className="px-4 py-2.5 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 min-h-[44px] transition-colors"
              >
                {addReferenceMutation.isPending ? 'Adding...' : 'Add Reference'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Progress Modal */}
      <BatchProgressModal
        isOpen={showProgressModal}
        title={progressTitle}
        items={progressFiles}
        onClose={() => setShowProgressModal(false)}
      />

      {/* Date Edit Modal */}
      {showDateModal && (
        <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
          <div className="bg-card border border-border rounded-xl shadow-2xl p-6 max-w-md w-full">
            <h3 className="text-lg font-semibold text-slate-900 dark:text-slate-100 mb-4 flex items-center space-x-2">
              <CalendarClock className="w-5 h-5 text-amber-500" />
              <span>Edit Post Date</span>
            </h3>
            <p className="text-sm text-slate-600 dark:text-slate-400 mb-4">
              Change the post date for this file. This will update the database and optionally update file timestamps.
            </p>
            <div className="space-y-4">
              <div>
                <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
                  New Date and Time
                </label>
                <input
                  type="datetime-local"
                  value={newDate}
                  onChange={(e) => setNewDate(e.target.value)}
                  className="w-full px-3 py-2 border border-border rounded-lg bg-background text-foreground"
                  autoFocus
                />
              </div>
              <div className="flex items-center space-x-2">
                <input
                  type="checkbox"
                  id="updateFileTimestampsReview"
                  checked={updateFileTimestamps}
                  onChange={(e) => setUpdateFileTimestamps(e.target.checked)}
                  className="w-4 h-4 text-amber-600 border-slate-300 rounded focus:ring-amber-500"
                />
                <label htmlFor="updateFileTimestampsReview" className="text-sm text-slate-700 dark:text-slate-300">
                  Update file timestamps (EXIF/metadata and filesystem)
                </label>
              </div>
            </div>
            <div className="flex justify-end space-x-2 mt-6">
              <button
                onClick={() => {
                  setShowDateModal(false)
                  setNewDate('')
                  setEditingItemId(null)
                }}
                className="px-4 py-2.5 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg min-h-[44px] transition-colors"
              >
                Cancel
              </button>
              <button
                onClick={confirmDateChange}
                disabled={!newDate || updateDateMutation.isPending}
                className="px-4 py-2.5 bg-amber-600 text-white rounded-lg hover:bg-amber-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-2 min-h-[44px] transition-colors"
              >
                {updateDateMutation.isPending ? (
                  <>
                    <span className="animate-spin">⏳</span>
                    <span>Updating...</span>
                  </>
                ) : (
                  <span>Apply</span>
                )}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Copy to Private Gallery Modal */}
      <CopyToGalleryModal
        open={showCopyToGalleryModal}
        onClose={() => setShowCopyToGalleryModal(false)}
        sourcePaths={Array.from(selectedPaths.values())}
        sourceType="review"
        onSuccess={() => {
          setShowCopyToGalleryModal(false)
          setSelectedItems(new Set())
          setSelectedPaths(new Map())
          setSelectMode(false)
        }}
      />
    </div>
  )
}
