import { useEffect, useMemo, useState, useRef } from 'react'
import { useSearchParams } from 'react-router-dom'
import facesApi, { FaceItem, SimilarFaceItem } from '../api/faces'
import peopleApi, { Person } from '../api/people'
import { apiClient } from '../api/client'
import tagsApi, { TagResponse } from '../api/tags'
import { useDeveloperMode } from '../context/DeveloperModeContext'

type SortBy = 'quality' | 'date_taken' | 'date_added'
type SortDir = 'asc' | 'desc'

export default function Identify() {
  const { isDeveloperMode } = useDeveloperMode()
  const [searchParams, setSearchParams] = useSearchParams()
  const [faces, setFaces] = useState<FaceItem[]>([])
  const [, setTotal] = useState(0)
  const [pageSize, setPageSize] = useState(50)
  const [minQuality, setMinQuality] = useState(0.0)
  const [sortBy, setSortBy] = useState<SortBy>('quality')
  const [sortDir, setSortDir] = useState<SortDir>('desc')
  const [dateFrom, setDateFrom] = useState<string>('')
  const [dateTo, setDateTo] = useState<string>('')
  // dateProcessed filter is hidden, so state removed
  // const [dateProcessed, setDateProcessed] = useState<string>('')
  
  // Parse photo_ids from URL parameter
  const photoIds = useMemo(() => {
    const photoIdsParam = searchParams.get('photo_ids')
    if (!photoIdsParam) return null
    const ids = photoIdsParam.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id))
    return ids.length > 0 ? ids : null
  }, [searchParams])

  const [currentIdx, setCurrentIdx] = useState(0)
  const currentFace = faces[currentIdx]

  const [similar, setSimilar] = useState<SimilarFaceItem[]>([])
  const [compareEnabled, setCompareEnabled] = useState(true)
  const [selectedSimilar, setSelectedSimilar] = useState<Record<number, boolean>>({})
  const [uniqueFacesOnly, setUniqueFacesOnly] = useState(true)
  
  // SessionStorage key for persisting settings (clears when tab/window closes)
  const SETTINGS_KEY = 'identify_settings'
  // SessionStorage key for persisting page state (faces, current index, etc.)
  const STATE_KEY = 'identify_state'

  const [people, setPeople] = useState<Person[]>([])
  const [personId, setPersonId] = useState<number | undefined>(undefined)
  const [firstName, setFirstName] = useState('')
  const [lastName, setLastName] = useState('')
  const [middleName, setMiddleName] = useState('')
  const [maidenName, setMaidenName] = useState('')
  const [dob, setDob] = useState('')
  const [busy, setBusy] = useState(false)
  const [imageLoading, setImageLoading] = useState(false)
  const [filtersCollapsed, setFiltersCollapsed] = useState(true)
  const [loadingFaces, setLoadingFaces] = useState(false)
  const [availableTags, setAvailableTags] = useState<TagResponse[]>([])
  const [selectedTags, setSelectedTags] = useState<string[]>([])
  const [tagsExpanded, setTagsExpanded] = useState(false)
  const [selectKey, setSelectKey] = useState(0) // Key to force select re-render when clearing
  
  // Store form data per face ID (matching desktop behavior)
  const [faceFormData, setFaceFormData] = useState<Record<number, {
    personId?: number
    firstName: string
    lastName: string
    middleName: string
    maidenName: string
    dob: string
  }>>({})
  
  // Track previous face ID to save data on navigation
  const prevFaceIdRef = useRef<number | undefined>(undefined)
  // Track if initial load has happened
  const initialLoadRef = useRef(false)
  // Track if settings have been loaded from localStorage
  const [settingsLoaded, setSettingsLoaded] = useState(false)
  // Track if state has been restored from sessionStorage
  const [stateRestored, setStateRestored] = useState(false)
  // Track if initial restoration is complete (prevents reload effects from firing during restoration)
  const restorationCompleteRef = useRef(false)

  const canIdentify = useMemo(() => {
    return Boolean((personId && currentFace) || (firstName && lastName && dob && currentFace))
  }, [personId, firstName, lastName, dob, currentFace])

  const loadFaces = async (clearState: boolean = false, ignorePhotoIds: boolean = false) => {
    setLoadingFaces(true)
    
    try {
      // Clear saved state if explicitly requested (Refresh button)
      if (clearState) {
        sessionStorage.removeItem(STATE_KEY)
      }
      
      const res = await facesApi.getUnidentified({
        page: 1,
        page_size: pageSize,
        min_quality: minQuality,
        date_taken_from: dateFrom || undefined,
        date_taken_to: dateTo || undefined,
        // date_processed filter is hidden, so don't send it
        // date_processed: dateProcessed || undefined,
        sort_by: sortBy,
        sort_dir: sortDir,
        tag_names: selectedTags.length > 0 ? selectedTags.join(', ') : undefined,
        match_all: false, // Default to match any tag
        photo_ids: (ignorePhotoIds || !photoIds) ? undefined : photoIds.join(','), // Filter by photo IDs if provided and not ignored
      })
      
      // Apply unique faces filter if enabled
      if (uniqueFacesOnly) {
        const filtered = await filterUniqueFaces(res.items)
        setFaces(filtered)
        setTotal(filtered.length)
      } else {
        setFaces(res.items)
        setTotal(res.total)
      }
      setCurrentIdx(0)
      // Clear form data when refreshing
      if (clearState) {
        setFaceFormData({})
        setSimilar([])
        setSelectedSimilar({})
      }
    } finally {
      setLoadingFaces(false)
    }
  }

  const filterUniqueFaces = async (faces: FaceItem[]): Promise<FaceItem[]> => {
    if (faces.length < 2) return faces
    
    // Create a map of face IDs to face objects for quick lookup
    const faceMap = new Map(faces.map(f => [f.id, f]))
    
    // Build similarity graph: use batch endpoint to get all similarities at once
    const similarityMap = new Map<number, Set<number>>()
    
    // Initialize similarity map for all faces
    for (const face of faces) {
      similarityMap.set(face.id, new Set<number>())
    }
    
    try {
      // Get all face IDs
      const faceIds = faces.map(f => f.id)
      
      // Call batch similarity endpoint - loads all faces once from DB
      // Note: This is where the heavy computation happens (comparing N faces to M faces)
      const batchRes = await facesApi.batchSimilarity({
        face_ids: faceIds,
        min_confidence: 60.0
      })
      
      // Build similarity map from batch results
      // Note: results include similarities to all faces in DB, but we only care about
      // similarities between faces in the current list
      let processedPairs = 0
      for (const pair of batchRes.pairs) {
        // Only include pairs where both faces are in the current list
        if (!faceMap.has(pair.face_id_1) || !faceMap.has(pair.face_id_2)) {
          processedPairs++
          // Allow UI to update periodically
          if (processedPairs % 100 === 0 || processedPairs === batchRes.pairs.length) {
            await new Promise(resolve => setTimeout(resolve, 0))
          }
          continue
        }
        
        // Add bidirectional relationships
        const set1 = similarityMap.get(pair.face_id_1) || new Set<number>()
        set1.add(pair.face_id_2)
        similarityMap.set(pair.face_id_1, set1)
        
        const set2 = similarityMap.get(pair.face_id_2) || new Set<number>()
        set2.add(pair.face_id_1)
        similarityMap.set(pair.face_id_2, set2)
        
        processedPairs++
        // Allow UI to update periodically
        if (processedPairs % 100 === 0 || processedPairs === batchRes.pairs.length) {
          await new Promise(resolve => setTimeout(resolve, 0))
        }
      }
    } catch (error) {
      // Silently skip on error - return original faces
      console.error('Error calculating batch similarities:', error)
      return faces
    }
    
    // Find connected components (groups of similar faces)
    const visited = new Set<number>()
    const groups: Set<number>[] = []
    
    const findGroup = (faceId: number, currentGroup: Set<number>) => {
      if (visited.has(faceId)) return
      visited.add(faceId)
      currentGroup.add(faceId)
      
      const similar = similarityMap.get(faceId) || new Set()
      for (const similarId of similar) {
        if (faceMap.has(similarId) && !visited.has(similarId)) {
          findGroup(similarId, currentGroup)
        }
      }
    }
    
    for (const face of faces) {
      if (!visited.has(face.id)) {
        const group = new Set<number>()
        findGroup(face.id, group)
        if (group.size > 1) {
          groups.push(group)
        }
      }
    }
    
    // Track which faces should be excluded (duplicates in groups)
    const excludeSet = new Set<number>()
    
    // For each group, keep only the first face and mark others for exclusion
    for (const group of groups) {
      let firstFaceFound = false
      for (const face of faces) {
        if (group.has(face.id)) {
          if (!firstFaceFound) {
            // Keep this face (first representative)
            firstFaceFound = true
          } else {
            // Exclude this face (duplicate)
            excludeSet.add(face.id)
          }
        }
      }
    }
    
    // Return faces that are not excluded
    return faces.filter(face => !excludeSet.has(face.id))
  }

  const loadPeople = async () => {
    const res = await peopleApi.list()
    setPeople(res.items)
  }

  const loadTags = async () => {
    try {
      const res = await tagsApi.list()
      setAvailableTags(res.items)
    } catch (error) {
      console.error('Error loading tags:', error)
    }
  }

  const loadSimilar = async (faceId: number) => {
    if (!compareEnabled) {
      setSimilar([])
      return
    }
    try {
      const res = await facesApi.getSimilar(faceId)
      setSimilar(res.items || [])
      setSelectedSimilar({})
    } catch (error) {
      setSimilar([])
    }
  }

  // Load settings from sessionStorage on mount
  // If photoIds is provided, skip loading filter settings (we want to show all faces from those photos)
  useEffect(() => {
    try {
      // If photoIds is in URL, don't load filter settings - we'll use defaults to show all faces
      if (photoIds !== null) {
        setSettingsLoaded(true)
        return
      }
      
      const saved = sessionStorage.getItem(SETTINGS_KEY)
      if (saved) {
        const settings = JSON.parse(saved)
        if (settings.pageSize !== undefined) setPageSize(settings.pageSize)
        if (settings.minQuality !== undefined) setMinQuality(settings.minQuality)
        if (settings.sortBy !== undefined) setSortBy(settings.sortBy)
        if (settings.sortDir !== undefined) setSortDir(settings.sortDir)
        if (settings.dateFrom !== undefined) setDateFrom(settings.dateFrom)
        if (settings.dateTo !== undefined) setDateTo(settings.dateTo)
        // dateProcessed filter is hidden, so don't load it from localStorage
        // if (settings.dateProcessed !== undefined) setDateProcessed(settings.dateProcessed)
        if (settings.uniqueFacesOnly !== undefined) setUniqueFacesOnly(settings.uniqueFacesOnly)
        if (settings.compareEnabled !== undefined) setCompareEnabled(settings.compareEnabled)
        if (settings.selectedTags !== undefined) setSelectedTags(settings.selectedTags)
      }
    } catch (error) {
      console.error('Error loading settings from sessionStorage:', error)
    } finally {
      setSettingsLoaded(true)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [photoIds])

  // Load state from sessionStorage on mount (faces, current index, similar, form data)
  // Skip state restoration if photoIds is provided in URL (we want fresh filtered data)
  useEffect(() => {
    try {
      // If photoIds is in URL, don't restore state - we need to load fresh filtered faces
      if (photoIds !== null) {
        setStateRestored(true)
        return
      }
      
      const saved = sessionStorage.getItem(STATE_KEY)
      if (saved) {
        const state = JSON.parse(saved)
        if (state.faces && Array.isArray(state.faces) && state.faces.length > 0) {
          setFaces(state.faces)
          if (state.currentIdx !== undefined) {
            setCurrentIdx(Math.min(state.currentIdx, state.faces.length - 1))
          }
          if (state.similar && Array.isArray(state.similar)) {
            setSimilar(state.similar)
          }
          if (state.faceFormData && typeof state.faceFormData === 'object') {
            setFaceFormData(state.faceFormData)
          }
          if (state.selectedSimilar && typeof state.selectedSimilar === 'object') {
            setSelectedSimilar(state.selectedSimilar)
          }
          // Mark that we restored state, so we don't reload
          initialLoadRef.current = true
          // Mark restoration as complete after state is restored
          // Use a small delay to ensure all state updates have been processed
          setTimeout(() => {
            restorationCompleteRef.current = true
          }, 50)
        }
      }
    } catch (error) {
      console.error('Error loading state from sessionStorage:', error)
    } finally {
      setStateRestored(true)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [photoIds])

  // Save state to sessionStorage whenever it changes (but only after initial restore)
  useEffect(() => {
    if (!stateRestored) return // Don't save during initial restore
    
    try {
      const state = {
        faces,
        currentIdx,
        similar,
        faceFormData,
        selectedSimilar,
      }
      sessionStorage.setItem(STATE_KEY, JSON.stringify(state))
    } catch (error) {
      console.error('Error saving state to sessionStorage:', error)
    }
  }, [faces, currentIdx, similar, faceFormData, selectedSimilar, stateRestored])

  // Save state on unmount (when navigating away) - use refs to capture latest values
  const facesRef = useRef(faces)
  const currentIdxRef = useRef(currentIdx)
  const similarRef = useRef(similar)
  const faceFormDataRef = useRef(faceFormData)
  const selectedSimilarRef = useRef(selectedSimilar)

  // Update refs whenever state changes
  useEffect(() => {
    facesRef.current = faces
    currentIdxRef.current = currentIdx
    similarRef.current = similar
    faceFormDataRef.current = faceFormData
    selectedSimilarRef.current = selectedSimilar
  }, [faces, currentIdx, similar, faceFormData, selectedSimilar])

  // Save state on unmount (when navigating away)
  useEffect(() => {
    return () => {
      try {
        const state = {
          faces: facesRef.current,
          currentIdx: currentIdxRef.current,
          similar: similarRef.current,
          faceFormData: faceFormDataRef.current,
          selectedSimilar: selectedSimilarRef.current,
        }
        sessionStorage.setItem(STATE_KEY, JSON.stringify(state))
      } catch (error) {
        console.error('Error saving state on unmount:', error)
      }
    }
  }, [])

  // Save settings to sessionStorage whenever they change (but only after initial load)
  useEffect(() => {
    if (!settingsLoaded) return // Don't save during initial load
    try {
      const settings = {
        pageSize,
        minQuality,
        sortBy,
        sortDir,
        dateFrom,
        dateTo,
        // dateProcessed filter is hidden, so don't save it
        // dateProcessed,
        uniqueFacesOnly,
        compareEnabled,
        selectedTags,
      }
      sessionStorage.setItem(SETTINGS_KEY, JSON.stringify(settings))
    } catch (error) {
      console.error('Error saving settings to sessionStorage:', error)
    }
  }, [pageSize, minQuality, sortBy, sortDir, dateFrom, dateTo, uniqueFacesOnly, compareEnabled, selectedTags, settingsLoaded])

  // Load tags and people on mount (always, regardless of other conditions)
  useEffect(() => {
    if (settingsLoaded) {
      loadPeople()
      loadTags()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [settingsLoaded])

  // Reset filters when photoIds is provided (to ensure all faces from those photos are shown)
  useEffect(() => {
    if (photoIds !== null && settingsLoaded) {
      // Reset filters to defaults to show all faces from the selected photos
      setMinQuality(0.0)
      setDateFrom('')
      setDateTo('')
      setSelectedTags([])
      // Keep uniqueFacesOnly as is (user preference)
      // Keep sortBy/sortDir as defaults (quality desc)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [photoIds, settingsLoaded])

  // Initial load on mount (after settings and state are loaded)
  useEffect(() => {
    if (!initialLoadRef.current && settingsLoaded && stateRestored) {
      initialLoadRef.current = true
      // Always load if photoIds is provided (from URL) - we need fresh filtered faces
      // Otherwise, only load if we didn't restore state (no faces means we need to load)
      if (photoIds !== null || faces.length === 0) {
        loadFaces()
        // If we're loading fresh, mark restoration as complete immediately
        restorationCompleteRef.current = true
      } else {
        // If state was restored, restorationCompleteRef is already set in the state restoration effect
        // But ensure it's set in case state restoration didn't happen
        if (!restorationCompleteRef.current) {
          setTimeout(() => {
            restorationCompleteRef.current = true
          }, 50)
        }
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [settingsLoaded, stateRestored, photoIds])
  
  // Reload when uniqueFacesOnly changes (immediate reload)
  // But only if restoration is complete (prevents reload during initial restoration)
  useEffect(() => {
    if (initialLoadRef.current && restorationCompleteRef.current) {
      loadFaces()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [uniqueFacesOnly])

  // Reload when pageSize changes (immediate reload)
  // But only if restoration is complete (prevents reload during initial restoration)
  useEffect(() => {
    if (initialLoadRef.current && restorationCompleteRef.current) {
      loadFaces()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [pageSize])

  useEffect(() => {
    if (currentFace && restorationCompleteRef.current) {
      setImageLoading(true) // Show loading indicator when face changes
      loadSimilar(currentFace.id)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentFace?.id, compareEnabled])

  // Preload images for next/previous faces
  useEffect(() => {
    if (!currentFace || faces.length === 0) return

    const preloadImages = () => {
      const preloadUrls: string[] = []
      const baseUrl = apiClient.defaults.baseURL || 'http://127.0.0.1:8000'
      
      // Preload next face
      if (currentIdx + 1 < faces.length) {
        const nextFace = faces[currentIdx + 1]
        preloadUrls.push(`${baseUrl}/api/v1/faces/${nextFace.id}/crop`)
      }
      
      // Preload previous face
      if (currentIdx > 0) {
        const prevFace = faces[currentIdx - 1]
        preloadUrls.push(`${baseUrl}/api/v1/faces/${prevFace.id}/crop`)
      }
      
      // Preload images in background
      preloadUrls.forEach(url => {
        const img = new Image()
        img.src = url
      })
    }

    // Small delay to avoid blocking current image load
    const timer = setTimeout(preloadImages, 100)
    return () => clearTimeout(timer)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentIdx, faces])

  // Save form data whenever fields change (for current face)
  useEffect(() => {
    if (!currentFace) return
    
    setFaceFormData((prev) => ({
      ...prev,
      [currentFace.id]: {
        personId,
        firstName,
        lastName,
        middleName,
        maidenName,
        dob,
      },
    }))
  }, [currentFace?.id, personId, firstName, lastName, middleName, maidenName, dob])

  // Restore form data when face changes (matching desktop behavior)
  useEffect(() => {
    if (!currentFace) {
      // Clear form when no face
      setPersonId(undefined)
      setFirstName('')
      setLastName('')
      setMiddleName('')
      setMaidenName('')
      setDob('')
      prevFaceIdRef.current = undefined
      return
    }

    // Don't restore if we're just setting the initial face
    if (prevFaceIdRef.current === currentFace.id) {
      return
    }

    // Restore saved form data for this face
    const saved = faceFormData[currentFace.id]
    if (saved) {
      setPersonId(saved.personId)
      setFirstName(saved.firstName)
      setLastName(saved.lastName)
      setMiddleName(saved.middleName)
      setMaidenName(saved.maidenName)
      setDob(saved.dob)
    } else {
      // No saved data - clear form
      setPersonId(undefined)
      setFirstName('')
      setLastName('')
      setMiddleName('')
      setMaidenName('')
      setDob('')
    }
    
    prevFaceIdRef.current = currentFace.id
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentFace?.id]) // Only restore when face ID changes


  const handleIdentify = async () => {
    if (!currentFace) return
    setBusy(true)
    const additional = Object.entries(selectedSimilar)
      .filter(([, v]) => v)
      .map(([k]) => Number(k))
    try {
      const payload: any = { additional_face_ids: additional }
      if (personId) {
        payload.person_id = personId
      } else {
        payload.first_name = firstName
        payload.last_name = lastName
        payload.middle_name = middleName || undefined
        payload.maiden_name = maidenName || undefined
        payload.date_of_birth = dob
      }
      await facesApi.identify(currentFace.id, payload)
      // Optimistic: remove identified faces from list
      const identifiedSet = new Set([currentFace.id, ...additional])
      const remaining = faces.filter((f) => !identifiedSet.has(f.id))
      setFaces(remaining)
      setCurrentIdx((i) => Math.min(i, Math.max(0, remaining.length - 1)))
      setSimilar([])
      setSelectedSimilar({})
      
      // Remove form data for identified faces (they're gone from the list)
      setFaceFormData((prev) => {
        const updated = { ...prev }
        identifiedSet.forEach((faceId) => delete updated[faceId])
        return updated
      })
      
      // Refresh people list if we created a new person
      if (!personId) {
        loadPeople()
      }
      
      // Don't clear form - let the useEffect handle restoring/clearing when face changes
    } finally {
      setBusy(false)
    }
  }

  const currentInfo = useMemo(() => {
    if (!currentFace) return ''
    return `Face ${currentIdx + 1} of ${faces.length}`
  }, [currentFace, currentIdx, faces.length])

  return (
    <div>
      <h1 className="text-2xl font-bold text-gray-900 mb-4">
        Identify
        {photoIds && (
          <span className="ml-2 text-sm font-normal text-gray-600">
            (Filtered by {photoIds.length} photo{photoIds.length !== 1 ? 's' : ''})
          </span>
        )}
      </h1>
      
      <div className="grid grid-cols-12 gap-4">
        {/* Left: Controls and current face */}
        <div className="col-span-4">
          {/* Unique Faces Checkbox and Batch Size - Outside Filters */}
          <div className="bg-white rounded-lg shadow mb-4 p-4">
            <div className="flex items-center justify-between gap-4">
              <div className="flex-1">
                <label className="flex items-center gap-2">
                  <input
                    type="checkbox"
                    checked={uniqueFacesOnly}
                    onChange={(e) => setUniqueFacesOnly(e.target.checked)}
                    className="rounded"
                  />
                  <span className="text-sm font-medium text-gray-700">Unique faces only</span>
                </label>
                <p className="text-xs text-gray-500 mt-1 ml-6">
                  Hide duplicates with ≥60% match confidence
                </p>
              </div>
              <div className="flex-shrink-0">
                <label className="block text-sm font-medium text-gray-700 mb-1">Batch Size</label>
                <select value={pageSize} onChange={(e) => setPageSize(parseInt(e.target.value))}
                  className="block w-full border rounded px-2 py-1 text-sm">
                  {[25, 50, 100, 200, 500, 1000, 1500, 2000].map((n) => (
                    <option key={n} value={n}>{n}</option>
                  ))}
                </select>
              </div>
            </div>
          </div>

          <div className="bg-white rounded-lg shadow mb-4">
            <div className="flex items-center justify-between p-4 border-b cursor-pointer hover:bg-gray-50" onClick={() => setFiltersCollapsed(!filtersCollapsed)}>
              <h2 className="text-lg font-semibold text-gray-900">Filters</h2>
              <button 
                className="text-gray-500 hover:text-gray-700 focus:outline-none"
                onClick={(e) => {
                  e.stopPropagation()
                  setFiltersCollapsed(!filtersCollapsed)
                }}
                aria-label={filtersCollapsed ? 'Expand filters' : 'Collapse filters'}
              >
                <span className="text-sm">{filtersCollapsed ? '▼' : '▲'}</span>
              </button>
            </div>
            {!filtersCollapsed && (
              <div className="p-4">
                <div className="grid grid-cols-2 gap-3">
              <div>
                <label className="block text-sm font-medium text-gray-700">Date Taken From</label>
                <input type="date" value={dateFrom} onChange={(e) => setDateFrom(e.target.value)}
                  className="mt-1 block w-full border rounded px-2 py-1" />
              </div>
              <div>
                <label className="block text-sm font-medium text-gray-700">Date Taken To</label>
                <input type="date" value={dateTo} onChange={(e) => setDateTo(e.target.value)}
                  className="mt-1 block w-full border rounded px-2 py-1" />
              </div>
              <div>
                <label className="block text-sm font-medium text-gray-700">Sort By</label>
                <select value={sortBy} onChange={(e) => setSortBy(e.target.value as SortBy)}
                  className="mt-1 block w-full border rounded px-2 py-1">
                  <option value="quality">Quality</option>
                  <option value="date_taken">Date Taken</option>
                  <option value="date_added">Date Processed</option>
                </select>
              </div>
              <div>
                <label className="block text-sm font-medium text-gray-700">Sort Dir</label>
                <select value={sortDir} onChange={(e) => setSortDir(e.target.value as SortDir)}
                  className="mt-1 block w-full border rounded px-2 py-1">
                  <option value="desc">Desc</option>
                  <option value="asc">Asc</option>
                </select>
              </div>
              {/* Date Processed filter hidden for now */}
              {/* <div>
                <label className="block text-sm font-medium text-gray-700">Date Processed On</label>
                <input type="date" value={dateProcessed} onChange={(e) => setDateProcessed(e.target.value)}
                  className="mt-1 block w-full border rounded px-2 py-1" />
              </div> */}
              <div>
                <label className="block text-sm font-medium text-gray-700">Min Quality</label>
                <input type="range" min={0} max={1} step={0.05} value={minQuality}
                  onChange={(e) => setMinQuality(parseFloat(e.target.value))} className="w-full" />
                <div className="text-xs text-gray-500">{(minQuality * 100).toFixed(0)}%</div>
              </div>
            </div>
                <div className="mt-4 pt-3 border-t">
                  <div className="flex items-center gap-2 mb-2">
                    <h3 className="text-sm font-medium text-gray-700">With Tags</h3>
                    <button
                      onClick={() => setTagsExpanded(!tagsExpanded)}
                      className="text-lg text-gray-600 hover:text-gray-800"
                      title={tagsExpanded ? 'Collapse' : 'Expand'}
                    >
                      {tagsExpanded ? '▼' : '▶'}
                    </button>
                  </div>
                  {tagsExpanded && (
                    <div className="mt-2">
                      <div className="flex gap-2">
                        <select
                          key={selectKey}
                          multiple
                          value={selectedTags}
                          onChange={(e) => {
                            const selected = Array.from(e.target.selectedOptions, option => option.value)
                            setSelectedTags(selected)
                          }}
                          className="flex-1 border rounded px-3 py-2 min-h-[120px]"
                          size={Math.min(availableTags.length, 8)}
                        >
                          {availableTags.map(tag => (
                            <option key={tag.id} value={tag.tag_name}>
                              {tag.tag_name}
                            </option>
                          ))}
                        </select>
                        <button
                          onClick={() => {
                            setSelectedTags([])
                            setSelectKey(prev => prev + 1) // Force select to re-render and clear visual selection
                          }}
                          disabled={selectedTags.length === 0}
                          className="px-3 py-2 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 disabled:bg-gray-50 disabled:text-gray-400 disabled:cursor-not-allowed text-sm self-start"
                          title="Clear all tag selections"
                        >
                          Clear
                        </button>
                      </div>
                      <p className="text-xs text-gray-500 mt-1">
                        Hold Ctrl/Cmd to select multiple tags
                      </p>
                    </div>
                  )}
                </div>
                <div className="mt-4 pt-3 border-t">
                  <div className="flex gap-2">
                    <button
                      onClick={() => loadFaces(false)}
                      disabled={loadingFaces}
                      className="flex-1 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed font-medium"
                    >
                      {loadingFaces ? 'Loading...' : 'Apply Filters'}
                    </button>
                  </div>
                </div>
              </div>
            )}
          </div>

          <div className="bg-white rounded-lg shadow p-4">
            <div className="flex items-center justify-between mb-2">
              <div className="text-sm text-gray-600">{currentInfo}</div>
              <div className="space-x-2">
                <button className="px-2 py-1 text-sm border rounded" onClick={() => setCurrentIdx((i) => Math.max(0, i - 1))}>Prev</button>
                <button className="px-2 py-1 text-sm border rounded" onClick={() => setCurrentIdx((i) => Math.min(faces.length - 1, i + 1))}>Next</button>
                <button 
                  className="px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700 disabled:bg-gray-400 disabled:cursor-not-allowed" 
                  onClick={() => {
                    // Clear photo_ids from URL if present
                    if (photoIds !== null) {
                      const newParams = new URLSearchParams(searchParams)
                      newParams.delete('photo_ids')
                      setSearchParams(newParams, { replace: true })
                      // Load faces without photo_ids filter (ignorePhotoIds = true)
                      loadFaces(true, true)
                    } else {
                      loadFaces(true)
                    }
                  }}
                  disabled={loadingFaces}
                  title="Refresh and start from beginning"
                >
                  {loadingFaces ? 'Refreshing...' : '🔄 Refresh'}
                </button>
              </div>
            </div>
            {!currentFace ? (
              <div className="text-gray-500">No faces to identify.</div>
            ) : (
              <div>
                <div 
                  className="aspect-video bg-gray-100 rounded mb-3 overflow-hidden flex items-center justify-center cursor-pointer hover:opacity-90 transition-opacity relative"
                  onClick={() => {
                    const photoUrl = `${apiClient.defaults.baseURL}/api/v1/photos/${currentFace.photo_id}/image`
                    window.open(photoUrl, '_blank')
                  }}
                  title="Click to open full photo"
                >
                  {imageLoading && (
                    <div className="absolute inset-0 flex items-center justify-center bg-gray-100 z-10">
                      <div className="text-gray-400 text-sm">Loading...</div>
                    </div>
                  )}
                  <img
                    key={currentFace.id}
                    src={`${apiClient.defaults.baseURL}/api/v1/faces/${currentFace.id}/crop`}
                    alt={`Face ${currentFace.id}`}
                    className="max-w-full max-h-full object-contain pointer-events-none"
                    crossOrigin="anonymous"
                    loading="eager"
                    onLoad={() => setImageLoading(false)}
                    onLoadStart={() => setImageLoading(true)}
                    onError={(e) => {
                      const target = e.target as HTMLImageElement
                      target.style.display = 'none'
                      setImageLoading(false)
                      const parent = target.parentElement
                      if (parent && !parent.querySelector('.error-fallback')) {
                        const fallback = document.createElement('div')
                        fallback.className = 'text-gray-400 error-fallback'
                        fallback.textContent = `Photo #${currentFace.photo_id}`
                        parent.appendChild(fallback)
                      }
                    }}
                  />
                </div>
                {/* Pose mode display */}
                {isDeveloperMode && currentFace.pose_mode && (
                  <div className="mb-2 text-sm text-gray-600">
                    <span className="font-medium">Pose:</span> {currentFace.pose_mode}
                  </div>
                )}
                <div className="grid grid-cols-2 gap-2">
                  <div className="col-span-2">
                    <label className="block text-sm font-medium text-gray-700">Select Existing Person (optional)</label>
                    <select value={personId ?? ''} onChange={(e) => {
                      const val = e.target.value ? parseInt(e.target.value) : undefined
                      setPersonId(val)
                      // Populate fields with selected person's data
                      if (val) {
                        const selectedPerson = people.find(p => p.id === val)
                        if (selectedPerson) {
                          setFirstName(selectedPerson.first_name || '')
                          setLastName(selectedPerson.last_name || '')
                          setMiddleName(selectedPerson.middle_name || '')
                          setMaidenName(selectedPerson.maiden_name || '')
                          setDob(selectedPerson.date_of_birth || '')
                        }
                      } else {
                        // Clear fields when selection is cleared
                        setFirstName('')
                        setLastName('')
                        setMiddleName('')
                        setMaidenName('')
                        setDob('')
                      }
                    }}
                      className="mt-1 block w-full border rounded px-2 py-1">
                      <option value="">— Or create new person below —</option>
                      {people.map((p) => (
                        <option key={p.id} value={p.id}>{p.last_name}, {p.first_name}</option>
                      ))}
                    </select>
                  </div>
                  <div className="col-span-2 border-t pt-2 mt-1">
                    <label className="block text-sm font-medium text-gray-700 mb-2">Create New Person</label>
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700">First Name *</label>
                    <input value={firstName} onChange={(e) => {
                      setFirstName(e.target.value)
                      setPersonId(undefined) // Clear person selection when typing
                    }}
                      className="mt-1 block w-full border rounded px-2 py-1"
                      readOnly={!!personId}
                      disabled={!!personId} />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700">Last Name *</label>
                    <input value={lastName} onChange={(e) => {
                      setLastName(e.target.value)
                      setPersonId(undefined) // Clear person selection when typing
                    }}
                      className="mt-1 block w-full border rounded px-2 py-1"
                      readOnly={!!personId}
                      disabled={!!personId} />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700">Middle Name</label>
                    <input value={middleName} onChange={(e) => {
                      setMiddleName(e.target.value)
                      setPersonId(undefined) // Clear person selection when typing
                    }}
                      className="mt-1 block w-full border rounded px-2 py-1"
                      readOnly={!!personId}
                      disabled={!!personId} />
                  </div>
                  <div>
                    <label className="block text-sm font-medium text-gray-700">Maiden Name</label>
                    <input value={maidenName} onChange={(e) => {
                      setMaidenName(e.target.value)
                      setPersonId(undefined) // Clear person selection when typing
                    }}
                      className="mt-1 block w-full border rounded px-2 py-1"
                      readOnly={!!personId}
                      disabled={!!personId} />
                  </div>
                  <div className="col-span-2">
                    <label className="block text-sm font-medium text-gray-700">Date of Birth *</label>
                    <input type="date" value={dob} onChange={(e) => {
                      setDob(e.target.value)
                      setPersonId(undefined) // Clear person selection when typing
                    }}
                      className="mt-1 block w-full border rounded px-2 py-1"
                      readOnly={!!personId}
                      disabled={!!personId} />
                  </div>
                  <div className="col-span-2 flex gap-2 mt-2">
                    <button disabled={!canIdentify || busy}
                      onClick={handleIdentify}
                      className={`px-3 py-2 rounded text-white ${canIdentify && !busy ? 'bg-indigo-600 hover:bg-indigo-700' : 'bg-gray-400 cursor-not-allowed'}`}>
                      {busy ? 'Identifying...' : 'Identify'}
                    </button>
                    <button onClick={() => setCurrentIdx((i) => Math.max(0, i - 1))} className="px-3 py-2 rounded border">Back</button>
                    <button onClick={() => setCurrentIdx((i) => Math.min(faces.length - 1, i + 1))} className="px-3 py-2 rounded border">Next</button>
                  </div>
                </div>
              </div>
            )}
          </div>
        </div>

        {/* Right: Similar faces */}
        <div className="col-span-8">
          <div className="bg-white rounded-lg shadow p-4">
            <div className="flex items-center justify-between mb-3">
              <div className="flex items-center gap-2">
                <input id="compare" type="checkbox" checked={compareEnabled}
                  onChange={(e) => setCompareEnabled(e.target.checked)} />
                <label htmlFor="compare" className="text-sm text-gray-700">Compare with similar faces</label>
              </div>
              <div className="space-x-2">
                <button className="px-2 py-1 text-sm border rounded"
                  onClick={() => setSelectedSimilar(Object.fromEntries(similar.map(s => [s.id, true])))}
                  disabled={!compareEnabled || similar.length === 0}>Select All</button>
                <button className="px-2 py-1 text-sm border rounded"
                  onClick={() => setSelectedSimilar({})}
                  disabled={!compareEnabled || similar.length === 0}>Clear All</button>
              </div>
            </div>
            {!compareEnabled ? (
              <div className="text-gray-500 py-4 text-center">Enable 'Compare similar faces' to see similar faces</div>
            ) : similar.length === 0 ? (
              <div className="text-gray-500 py-4 text-center">No similar faces found</div>
            ) : (
              <div className="space-y-2 max-h-[600px] overflow-y-auto similar-faces-scrollable">
                {similar.map((s) => {
                  // s.similarity is actually calibrated confidence in [0,1] range
                  // Desktop uses calibrated confidence from _get_calibrated_confidence
                  // Convert to percentage: confidence = similarity * 100
                  const confidencePct = Math.round(s.similarity * 100)
                  
                  // Get confidence description matching desktop
                  let confidenceDesc: string
                  let confidenceColor: string
                  if (confidencePct >= 80) {
                    confidenceDesc = "(Very High)"
                    confidenceColor = "text-green-600"
                  } else if (confidencePct >= 70) {
                    confidenceDesc = "(High)"
                    confidenceColor = "text-orange-600"
                  } else if (confidencePct >= 60) {
                    confidenceDesc = "(Medium)"
                    confidenceColor = "text-red-600"
                  } else if (confidencePct >= 50) {
                    confidenceDesc = "(Low)"
                    confidenceColor = "text-red-600"
                  } else {
                    confidenceDesc = "(Very Low)"
                    confidenceColor = "text-gray-500"
                  }
                  
                  return (
                    <div key={s.id} className="flex items-center gap-3 p-2 border rounded hover:bg-gray-50">
                      {/* Checkbox */}
                      <input
                        type="checkbox"
                        checked={!!selectedSimilar[s.id]}
                        onChange={(e) => setSelectedSimilar((prev) => ({ ...prev, [s.id]: e.target.checked }))}
                        className="flex-shrink-0"
                      />
                      
                      {/* Face image */}
                      <div
                        className="w-24 h-24 bg-gray-100 rounded overflow-hidden flex items-center justify-center relative group cursor-pointer flex-shrink-0"
                        onClick={(e) => {
                          e.stopPropagation()
                          const photoUrl = `${apiClient.defaults.baseURL}/api/v1/photos/${s.photo_id}/image`
                          window.open(photoUrl, '_blank')
                        }}
                        title="Click to open full photo"
                      >
                        <img
                          src={`${apiClient.defaults.baseURL}/api/v1/faces/${s.id}/crop`}
                          alt={`Face ${s.id}`}
                          className="max-w-full max-h-full object-contain pointer-events-none"
                          crossOrigin="anonymous"
                          loading="lazy"
                          onError={(e) => {
                            const target = e.target as HTMLImageElement
                            target.style.display = 'none'
                            const parent = target.parentElement
                            if (parent && !parent.querySelector('.error-fallback')) {
                              const fallback = document.createElement('div')
                              fallback.className = 'text-gray-400 text-xs error-fallback'
                              fallback.textContent = `#${s.photo_id}`
                              parent.appendChild(fallback)
                            }
                          }}
                        />
                        <div className="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-10 transition-opacity pointer-events-none" />
                      </div>
                      
                      {/* Confidence percentage with description */}
                      <div className={`text-sm font-bold ${confidenceColor} flex-shrink-0`}>
                        {confidencePct}% {confidenceDesc}
                      </div>
                      
                      {/* Pose mode */}
                      {isDeveloperMode && s.pose_mode && (
                        <div className="text-sm text-gray-600 flex-shrink-0">
                          {s.pose_mode}
                        </div>
                      )}
                      
                      {/* Filename */}
                      <div className="text-sm text-gray-700 flex-1 min-w-0 truncate" title={s.filename}>
                        {s.filename}
                      </div>
                    </div>
                  )
                })}
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  )
}


