import { useEffect, useMemo, useState, useRef, useCallback } 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 videosApi, { VideoListItem, VideoPersonInfo, IdentifyVideoRequest } from '../api/videos'
import { useDeveloperMode } from '../context/DeveloperModeContext'
import { useAuth } from '../context/AuthContext'
import pendingIdentificationsApi, {
  IdentificationReportResponse,
} from '../api/pendingIdentifications'

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

export default function Identify() {
  const { isDeveloperMode } = useDeveloperMode()
  const { isAdmin } = useAuth()
  const [searchParams, setSearchParams] = useSearchParams()
  const [faces, setFaces] = useState<FaceItem[]>([])
  const [, setTotal] = useState(0)
  const [page, setPage] = useState(1)
  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)
  
  // Excluded faces filter
  const [includeExcludedFaces, setIncludeExcludedFaces] = useState(false)
  
  // 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 [setupAreaCollapsed, setSetupAreaCollapsed] = useState(false)
  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

  // Identification statistics modal state
  const [showStats, setShowStats] = useState(false)
  const [statsData, setStatsData] = useState<IdentificationReportResponse | null>(null)
  const [statsLoading, setStatsLoading] = useState(false)
  const [statsError, setStatsError] = useState<string | null>(null)
  const [statsDateFrom, setStatsDateFrom] = useState<string>('')
  const [statsDateTo, setStatsDateTo] = useState<string>('')
  
  // Tab state
  const [activeTab, setActiveTab] = useState<'faces' | 'videos'>('faces')
  
  // Video identification state
  const [videos, setVideos] = useState<VideoListItem[]>([])
  const [videosTotal, setVideosTotal] = useState(0)
  const [videosPage, setVideosPage] = useState(1)
  const [videosPageSize, setVideosPageSize] = useState(50)
  const [videosLoading, setVideosLoading] = useState(false)
  const [selectedVideo, setSelectedVideo] = useState<VideoListItem | null>(null)
  const [videoPeople, setVideoPeople] = useState<VideoPersonInfo[]>([])
  const [videoPeopleLoading, setVideoPeopleLoading] = useState(false)
  const [videosFolderFilter, setVideosFolderFilter] = useState<string>('')
  const [videosDateFrom, setVideosDateFrom] = useState<string>('')
  const [videosDateTo, setVideosDateTo] = useState<string>('')
  const [videosHasPeople, setVideosHasPeople] = useState<boolean | undefined>(undefined)
  const [videosPersonName, setVideosPersonName] = useState<string>('')
  const [videosSortBy, setVideosSortBy] = useState<string>('filename')
  const [videosSortDir, setVideosSortDir] = useState<string>('asc')
  const [videosFiltersCollapsed, setVideosFiltersCollapsed] = useState(true)
  
  // Person identification form state
  const [videoPersonId, setVideoPersonId] = useState<number | undefined>(undefined)
  const [videoFirstName, setVideoFirstName] = useState('')
  const [videoLastName, setVideoLastName] = useState('')
  const [videoMiddleName, setVideoMiddleName] = useState('')
  const [videoMaidenName, setVideoMaidenName] = useState('')
  const [videoDob, setVideoDob] = useState('')
  const [videoIdentifying, setVideoIdentifying] = useState(false)
  
  // 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(() => {
    if (!currentFace) return false
    if (personId) return true
    return Boolean(firstName.trim() && lastName.trim())
  }, [personId, firstName, lastName, currentFace])

  const loadFaces = async (
    clearState: boolean = false,
    ignorePhotoIds: boolean = false,
    targetPage?: number
  ) => {
    setLoadingFaces(true)
    const pageToLoad = targetPage ?? page
    
    try {
      // Clear saved state if explicitly requested (Refresh button)
      if (clearState) {
        sessionStorage.removeItem(STATE_KEY)
      }
      
      const res = await facesApi.getUnidentified({
        page: pageToLoad,
        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
        include_excluded: includeExcludedFaces,
      })
      
      // Apply unique faces filter if enabled
      let filtered = res.items
      if (uniqueFacesOnly) {
        filtered = await filterUniqueFaces(filtered)
      }
      
      setFaces(filtered)
      setTotal(filtered.length)
      setPage(pageToLoad)
      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 loadStats = useCallback(async () => {
    setStatsLoading(true)
    setStatsError(null)
    try {
      const response = await pendingIdentificationsApi.getReport(
        statsDateFrom || undefined,
        statsDateTo || undefined
      )
      setStatsData(response)
    } catch (error: any) {
      setStatsError(error.response?.data?.detail || error.message || 'Failed to load report')
      console.error('Error loading identification report:', error)
    } finally {
      setStatsLoading(false)
    }
  }, [statsDateFrom, statsDateTo])

  const handleOpenStats = () => {
    setShowStats(true)
    loadStats()
  }

  const handleCloseStats = () => {
    setShowStats(false)
    setStatsData(null)
    setStatsError(null)
    setStatsDateFrom('')
    setStatsDateTo('')
  }

  const loadSimilar = async (faceId: number) => {
    if (!compareEnabled) {
      setSimilar([])
      return
    }
    try {
      const res = await facesApi.getSimilar(faceId, includeExcludedFaces)
      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)
    }
     
  }, [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)
          }
          if (state.page !== undefined) {
            setPage(state.page)
          }
          // 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)
    }
     
  }, [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,
        page,
      }
      sessionStorage.setItem(STATE_KEY, JSON.stringify(state))
    } catch (error) {
      console.error('Error saving state to sessionStorage:', error)
    }
  }, [faces, currentIdx, similar, faceFormData, selectedSimilar, page, 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)
  const pageRef = useRef(page)

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

  // 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,
          page: pageRef.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])

  // Reload faces when includeExcludedFaces changes
  useEffect(() => {
    if (settingsLoaded && !photoIds) {
      setPage(1)
      loadFaces(false, false, 1)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [includeExcludedFaces])

  // Load tags and people on mount (always, regardless of other conditions)
  useEffect(() => {
    if (settingsLoaded) {
      loadPeople()
      loadTags()
    }
     
  }, [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)
    }
     
  }, [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) {
      setPage(1)
      loadFaces(false, false, 1)
    }
    // 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) {
      setPage(1)
      loadFaces(false, false, 1)
    }
    // 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 () => {
    console.log('handleIdentify called', { currentFace, personId, firstName, lastName, canIdentify })
    
    if (!currentFace) {
      console.warn('handleIdentify: No current face')
      return
    }
    
    // Validate that we have either a person ID or both first and last name
    if (!personId && (!firstName.trim() || !lastName.trim())) {
      alert('Please select an existing person or enter first name and last name.')
      return
    }
    
    setBusy(true)
    const trimmedFirstName = firstName.trim()
    const trimmedLastName = lastName.trim()
    const trimmedMiddleName = middleName.trim()
    const trimmedMaidenName = maidenName.trim()
    const trimmedDob = dob.trim()
    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 = trimmedFirstName
        payload.last_name = trimmedLastName
        if (trimmedMiddleName) {
          payload.middle_name = trimmedMiddleName
        }
        if (trimmedMaidenName) {
          payload.maiden_name = trimmedMaidenName
        }
        if (trimmedDob) {
          payload.date_of_birth = trimmedDob
        }
      }
      
      console.log('Identifying face:', currentFace.id, 'with payload:', payload)
      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
    } catch (error: any) {
      console.error('Error identifying face:', error)
      const errorMessage = error.response?.data?.detail || error.message || 'Failed to identify face. Please try again.'
      alert(errorMessage)
    } finally {
      setBusy(false)
    }
  }

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

  const handleNextPage = () => {
    if (loadingFaces) return
    const nextPage = page + 1
    setPage(nextPage)
    loadFaces(false, false, nextPage)
  }
  const handlePrevPage = () => {
    if (loadingFaces || page <= 1) return
    const prevPage = Math.max(1, page - 1)
    setPage(prevPage)
    loadFaces(false, false, prevPage)
  }

  // Toggle excluded status for current face
  const toggleBlockFace = useCallback(async () => {
    if (!currentFace) return
    
    const newExcludedStatus = !currentFace.excluded
    
    try {
      await facesApi.setExcluded(currentFace.id, newExcludedStatus)
      
      // Update the face in the local state
      setFaces(prevFaces => 
        prevFaces.map(face => 
          face.id === currentFace.id ? { ...face, excluded: newExcludedStatus } : face
        )
      )
      
      // If excluding, move to next face if available
      if (newExcludedStatus && currentIdx + 1 < faces.length) {
        setCurrentIdx(currentIdx + 1)
      } else if (newExcludedStatus && currentIdx > 0) {
        setCurrentIdx(currentIdx - 1)
      }
      
      // Don't reload faces - keep UI as is
    } catch (error) {
      console.error('Error toggling excluded status:', error)
      alert('Failed to update excluded status')
    }
  }, [currentFace, currentIdx, faces.length])

  // Video functions
  const loadVideos = async () => {
    setVideosLoading(true)
    try {
      const response = await videosApi.listVideos({
        page: videosPage,
        page_size: videosPageSize,
        folder_path: videosFolderFilter || undefined,
        date_from: videosDateFrom || undefined,
        date_to: videosDateTo || undefined,
        has_people: videosHasPeople,
        person_name: videosPersonName || undefined,
        sort_by: videosSortBy,
        sort_dir: videosSortDir,
      })
      setVideos(response.items)
      setVideosTotal(response.total)
    } catch (error) {
      console.error('Failed to load videos:', error)
      alert('Failed to load videos. Please try again.')
    } finally {
      setVideosLoading(false)
    }
  }

  const loadVideoPeople = async (videoId: number) => {
    setVideoPeopleLoading(true)
    try {
      const response = await videosApi.getVideoPeople(videoId)
      setVideoPeople(response.people)
    } catch (error) {
      console.error('Failed to load video people:', error)
      alert('Failed to load people for this video.')
    } finally {
      setVideoPeopleLoading(false)
    }
  }

  const handleVideoSelect = async (video: VideoListItem) => {
    setSelectedVideo(video)
    await loadVideoPeople(video.id)
    // Clear form
    setVideoPersonId(undefined)
    setVideoFirstName('')
    setVideoLastName('')
    setVideoMiddleName('')
    setVideoMaidenName('')
    setVideoDob('')
  }

  const handleVideoIdentify = async () => {
    if (!selectedVideo) return
    
    const trimmedFirstName = videoFirstName.trim()
    const trimmedLastName = videoLastName.trim()
    const trimmedMiddleName = videoMiddleName.trim()
    const trimmedMaidenName = videoMaidenName.trim()
    const trimmedDob = videoDob.trim()
    
    if (!videoPersonId && (!trimmedFirstName || !trimmedLastName)) {
      alert('Please select an existing person or enter first name and last name.')
      return
    }
    
    setVideoIdentifying(true)
    try {
      const payload: IdentifyVideoRequest = {}
      if (videoPersonId) {
        payload.person_id = videoPersonId
      } else {
        payload.first_name = trimmedFirstName
        payload.last_name = trimmedLastName
        if (trimmedMiddleName) {
          payload.middle_name = trimmedMiddleName
        }
        if (trimmedMaidenName) {
          payload.maiden_name = trimmedMaidenName
        }
        if (trimmedDob) {
          payload.date_of_birth = trimmedDob
        }
      }
      
      await videosApi.identifyPerson(selectedVideo.id, payload)
      
      // Refresh video people list
      await loadVideoPeople(selectedVideo.id)
      
      // Refresh videos list to update people count
      await loadVideos()
      
      // Refresh people list if we created a new person
      if (!videoPersonId) {
        loadPeople()
      }
      
      // Clear form
      setVideoPersonId(undefined)
      setVideoFirstName('')
      setVideoLastName('')
      setVideoMiddleName('')
      setVideoMaidenName('')
      setVideoDob('')
    } catch (error: any) {
      console.error('Failed to identify person in video:', error)
      alert(error.response?.data?.detail || 'Failed to identify person in video. Please try again.')
    } finally {
      setVideoIdentifying(false)
    }
  }

  const handleRemovePersonFromVideo = async (personId: number) => {
    if (!selectedVideo) return
    
    if (!confirm('Remove this person from the video?')) {
      return
    }
    
    try {
      await videosApi.removePerson(selectedVideo.id, personId)
      
      // Refresh video people list
      await loadVideoPeople(selectedVideo.id)
      
      // Refresh videos list to update people count
      await loadVideos()
    } catch (error: any) {
      console.error('Failed to remove person from video:', error)
      alert(error.response?.data?.detail || 'Failed to remove person from video. Please try again.')
    }
  }

  // Load videos when tab is active
  useEffect(() => {
    if (activeTab === 'videos') {
      loadVideos()
      loadPeople() // Load people for the dropdown
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeTab, videosPage, videosPageSize, videosFolderFilter, videosDateFrom, videosDateTo, videosHasPeople, videosPersonName, videosSortBy, videosSortDir])

  return (
    <div>
      {photoIds && (
        <div className="mb-4">
          <span className="text-sm font-normal text-gray-600">
            (Filtered by {photoIds.length} photo{photoIds.length !== 1 ? 's' : ''})
          </span>
        </div>
      )}

      {/* Tabs */}
      <div className="mb-4 border-b border-gray-200">
        <div className="flex items-center justify-between">
          <nav className="flex space-x-8" aria-label="Tabs">
            <button
              onClick={() => setActiveTab('faces')}
              className={`py-4 px-1 border-b-2 font-medium text-sm ${
                activeTab === 'faces'
                  ? 'border-indigo-500 text-indigo-600'
                  : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
              }`}
            >
              Identify Faces
            </button>
            <button
              onClick={() => setActiveTab('videos')}
              className={`py-4 px-1 border-b-2 font-medium text-sm ${
                activeTab === 'videos'
                  ? 'border-indigo-500 text-indigo-600'
                  : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
              }`}
            >
              Identify People in Videos
            </button>
          </nav>
          {isAdmin && activeTab === 'faces' && (
            <button
              type="button"
              onClick={handleOpenStats}
              className="px-3 py-1.5 text-sm bg-green-100 text-green-700 rounded-md hover:bg-green-200 font-medium"
            >
              📊 Statistics
            </button>
          )}
        </div>
      </div>

      {/* Tab Content */}
      {activeTab === 'faces' && (
        <>
        <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 */}
          {!setupAreaCollapsed && (
          <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 className="flex items-center justify-between gap-3 mt-4">
              <div className="text-sm text-gray-600">Page {page}</div>
              <div className="flex gap-2">
                <button
                  onClick={handlePrevPage}
                  disabled={loadingFaces || page <= 1}
                  className="px-3 py-1.5 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 font-medium"
                >
                  Prev {pageSize}
                </button>
                <button
                  onClick={handleNextPage}
                  disabled={loadingFaces}
                  className="px-3 py-1.5 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 font-medium"
                >
                  {loadingFaces ? 'Loading...' : `Next ${pageSize}`}
                </button>
              </div>
            </div>
          </div>
          )}

          {!setupAreaCollapsed && (
          <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">
                  <label className="flex items-center gap-2">
                    <input
                      type="checkbox"
                      checked={includeExcludedFaces}
                      onChange={(e) => setIncludeExcludedFaces(e.target.checked)}
                      className="rounded"
                    />
                    <span className="text-sm font-medium text-gray-700">Include excluded faces</span>
                  </label>
                  <p className="text-xs text-gray-500 mt-1 ml-6">
                    Show faces that have been blocked/excluded from identification
                  </p>
                </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={() => {
                        setPage(1)
                        loadFaces(false, false, 1)
                      }}
                      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>
            )}
            {/* Collapse button at bottom of filters section */}
            <div className="border-t p-2 flex justify-center">
              <button
                onClick={() => setSetupAreaCollapsed(true)}
                className="text-gray-500 hover:text-gray-700 focus:outline-none flex items-center gap-1 text-sm"
                aria-label="Hide setup and filters"
                title="Hide setup and filters"
              >
                <span>▲</span>
                <span>Hide</span>
              </button>
            </div>
          </div>
          )}

          {/* Show expand button when collapsed */}
          {setupAreaCollapsed && (
            <div className="bg-white rounded-lg shadow mb-4 p-2 flex justify-center">
              <button
                onClick={() => setSetupAreaCollapsed(false)}
                className="text-gray-500 hover:text-gray-700 focus:outline-none flex items-center gap-1 text-sm"
                aria-label="Show setup and filters"
                title="Show setup and filters"
              >
                <span>▼</span>
                <span>Show Setup & Filters</span>
              </button>
            </div>
          )}

          <div className="bg-white rounded-lg shadow p-4">
            <div className="flex items-center justify-between mb-2">
              <div className="flex items-center gap-2">
                <span className="text-sm text-gray-600">{currentInfo}</span>
                {currentFace && (
                  <button
                    onClick={toggleBlockFace}
                    className={`px-2 py-1 text-sm rounded hover:bg-gray-100 ${
                      currentFace.excluded
                        ? 'bg-red-100 text-red-700'
                        : 'bg-gray-50 text-gray-600'
                    }`}
                    title={currentFace.excluded ? 'Include this face' : 'Exclude this face from identification'}
                  >
                    🚫
                  </button>
                )}
              </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)}
                    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 (optional)</label>
                    <input type="date" value={dob} onChange={(e) => {
                      setDob(e.target.value)
                      setPersonId(undefined) // Clear person selection when typing
                    }}
                      className="mt-1 block border rounded px-2 py-1 w-full max-w-[220px]"
                      readOnly={!!personId}
                      disabled={!!personId} />
                  </div>
                  <div className="col-span-2 flex gap-2 mt-2">
                    <button 
                      type="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 type="button" onClick={() => setCurrentIdx((i) => Math.max(0, i - 1))} className="px-3 py-2 rounded border">Back</button>
                    <button type="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 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 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>
            {!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>

        {/* Identification statistics modal */}
        {showStats && (
        <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
          <div className="bg-white rounded-lg shadow-xl max-w-4xl w-full mx-4 max-h-[90vh] overflow-hidden flex flex-col">
            <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
              <h2 className="text-xl font-bold text-gray-900">Identification Report</h2>
              <button
                type="button"
                onClick={handleCloseStats}
                className="text-gray-400 hover:text-gray-600 text-2xl font-bold"
              >
                ×
              </button>
            </div>

            <div className="px-6 py-4 border-b border-gray-200">
              <div className="flex items-center gap-4">
                <div className="flex-1">
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Date From
                  </label>
                  <input
                    type="date"
                    value={statsDateFrom}
                    onChange={(event) => setStatsDateFrom(event.target.value)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
                  />
                </div>
                <div className="flex-1">
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Date To
                  </label>
                  <input
                    type="date"
                    value={statsDateTo}
                    onChange={(event) => setStatsDateTo(event.target.value)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
                  />
                </div>
                <div className="flex items-end">
                  <button
                    type="button"
                    onClick={loadStats}
                    disabled={statsLoading}
                    className="px-3 py-1.5 text-sm bg-blue-100 text-blue-700 rounded-md hover:bg-blue-200 disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed font-medium"
                  >
                    {statsLoading ? 'Loading...' : 'Apply Filter'}
                  </button>
                </div>
              </div>
            </div>

            <div className="flex-1 overflow-y-auto px-6 py-4">
              {statsLoading && (
                <div className="text-center py-8">
                  <p className="text-gray-600">Loading report...</p>
                </div>
              )}

              {statsError && (
                <div className="mb-4 p-4 bg-red-50 border border-red-200 rounded text-red-700">
                  <p className="font-semibold">Error loading report</p>
                  <p className="text-sm mt-1">{statsError}</p>
                </div>
              )}

              {!statsLoading && !statsError && statsData && (
                <>
                  <div className="mb-4 p-4 bg-blue-50 border border-blue-200 rounded">
                    <div className="grid grid-cols-3 gap-4 text-sm">
                      <div>
                        <span className="font-semibold text-gray-700">Total Users:</span>{' '}
                        <span className="text-gray-900">{statsData.total_users}</span>
                      </div>
                      <div>
                        <span className="font-semibold text-gray-700">Total Faces:</span>{' '}
                        <span className="text-gray-900">{statsData.total_faces}</span>
                      </div>
                      <div>
                        <span className="font-semibold text-gray-700">Average per User:</span>{' '}
                        <span className="text-gray-900">
                          {statsData.total_users > 0
                            ? Math.round((statsData.total_faces / statsData.total_users) * 10) / 10
                            : 0}
                        </span>
                      </div>
                    </div>
                  </div>

                  {statsData.items.length === 0 ? (
                    <div className="text-center py-8 text-gray-500">
                      <p>No identifications found for the selected date range.</p>
                    </div>
                  ) : (
                    <div className="overflow-x-auto">
                      <table className="min-w-full divide-y divide-gray-200">
                        <thead className="bg-gray-50">
                          <tr>
                            <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                              User
                            </th>
                            <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                              Faces Identified
                            </th>
                            <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                              First Identification
                            </th>
                            <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                              Last Identification
                            </th>
                          </tr>
                        </thead>
                        <tbody className="bg-white divide-y divide-gray-200">
                          {statsData.items.map((item) => (
                            <tr key={item.user_id}>
                              <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
                                {item.full_name || item.username}
                              </td>
                              <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
                                {item.face_count}
                              </td>
                              <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
                                {item.first_identification_date || '-'}
                              </td>
                              <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
                                {item.last_identification_date || '-'}
                              </td>
                            </tr>
                          ))}
                        </tbody>
                      </table>
                    </div>
                  )}
                </>
              )}
            </div>
          </div>
        </div>
        )}
        </>
      )}

      {activeTab === 'videos' && (
        <div className="space-y-4">
          {/* Filters */}
          <div className="bg-white rounded-lg shadow p-4">
            <button
              onClick={() => setVideosFiltersCollapsed(!videosFiltersCollapsed)}
              className="flex items-center justify-between w-full text-left font-medium text-gray-700"
            >
              <span>Filters</span>
              <span>{videosFiltersCollapsed ? '▼' : '▲'}</span>
            </button>
            {!videosFiltersCollapsed && (
              <div className="mt-4 grid grid-cols-1 md:grid-cols-3 gap-4">
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Folder Path
                  </label>
                  <input
                    type="text"
                    value={videosFolderFilter}
                    onChange={(e) => setVideosFolderFilter(e.target.value)}
                    placeholder="Filter by folder path"
                    className="w-full px-3 py-2 border border-gray-300 rounded-md"
                  />
                </div>
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Date From
                  </label>
                  <input
                    type="date"
                    value={videosDateFrom}
                    onChange={(e) => setVideosDateFrom(e.target.value)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-md"
                  />
                </div>
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Date To
                  </label>
                  <input
                    type="date"
                    value={videosDateTo}
                    onChange={(e) => setVideosDateTo(e.target.value)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-md"
                  />
                </div>
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Person Name
                  </label>
                  <input
                    type="text"
                    value={videosPersonName}
                    onChange={(e) => setVideosPersonName(e.target.value)}
                    placeholder="Search by person name"
                    className="w-full px-3 py-2 border border-gray-300 rounded-md"
                  />
                </div>
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Has People
                  </label>
                  <select
                    value={videosHasPeople === undefined ? 'all' : videosHasPeople ? 'yes' : 'no'}
                    onChange={(e) => {
                      const val = e.target.value
                      setVideosHasPeople(val === 'all' ? undefined : val === 'yes')
                    }}
                    className="w-full px-3 py-2 border border-gray-300 rounded-md"
                  >
                    <option value="all">All</option>
                    <option value="yes">With People</option>
                    <option value="no">Without People</option>
                  </select>
                </div>
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Sort By
                  </label>
                  <select
                    value={videosSortBy}
                    onChange={(e) => setVideosSortBy(e.target.value)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-md"
                  >
                    <option value="filename">Filename</option>
                    <option value="date_taken">Date Taken</option>
                    <option value="date_added">Date Added</option>
                  </select>
                </div>
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    Sort Direction
                  </label>
                  <select
                    value={videosSortDir}
                    onChange={(e) => setVideosSortDir(e.target.value)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-md"
                  >
                    <option value="asc">Ascending</option>
                    <option value="desc">Descending</option>
                  </select>
                </div>
              </div>
            )}
          </div>

          <div className="grid grid-cols-12 gap-4">
            {/* Left: Video List */}
            <div className="col-span-5">
              <div className="bg-white rounded-lg shadow p-4">
                <div className="flex items-center justify-between mb-4">
                  <h3 className="text-lg font-semibold text-gray-700">
                    Videos ({videosTotal})
                  </h3>
                  <div className="flex items-center gap-2">
                    <label className="text-sm text-gray-600">Page Size:</label>
                    <select
                      value={videosPageSize}
                      onChange={(e) => {
                        setVideosPageSize(Number(e.target.value))
                        setVideosPage(1)
                      }}
                      className="px-2 py-1 border border-gray-300 rounded text-sm"
                    >
                      <option value={25}>25</option>
                      <option value={50}>50</option>
                      <option value={100}>100</option>
                    </select>
                  </div>
                </div>
                
                {videosLoading ? (
                  <div className="text-center py-8 text-gray-500">Loading videos...</div>
                ) : videos.length === 0 ? (
                  <div className="text-center py-8 text-gray-500">No videos found</div>
                ) : (
                  <div className="space-y-2 max-h-[600px] overflow-y-auto">
                    {videos.map((video) => (
                      <div key={video.id}>
                        <div
                          onClick={() => handleVideoSelect(video)}
                          className={`p-3 border rounded-lg cursor-pointer transition ${
                            selectedVideo?.id === video.id
                              ? 'border-indigo-500 bg-indigo-50'
                              : 'border-gray-200 hover:border-gray-300 hover:bg-gray-50'
                          }`}
                        >
                          <div className="flex items-start gap-3">
                            <img
                              src={videosApi.getThumbnailUrl(video.id)}
                              alt={video.filename}
                              className="w-20 h-15 object-cover rounded"
                              onError={(e) => {
                                (e.target as HTMLImageElement).src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="80" height="60"%3E%3Crect fill="%23ddd" width="80" height="60"/%3E%3Ctext x="50%25" y="50%25" text-anchor="middle" dy=".3em" fill="%23999" font-size="12"%3EVideo%3C/text%3E%3C/svg%3E'
                              }}
                            />
                            <div className="flex-1 min-w-0">
                              <div className="font-medium text-gray-900 truncate">
                                {video.filename}
                              </div>
                              {video.date_taken && (
                                <div className="text-sm text-gray-500">
                                  {new Date(video.date_taken).toLocaleDateString()}
                                </div>
                              )}
                              {video.identified_people_count > 0 && (
                                <div className="mt-1 flex flex-wrap gap-1">
                                  {video.identified_people.slice(0, 3).map((person) => (
                                    <span
                                      key={person.id}
                                      className="px-2 py-0.5 bg-indigo-100 text-indigo-700 text-xs rounded"
                                    >
                                      {person.first_name} {person.last_name}
                                    </span>
                                  ))}
                                  {video.identified_people_count > 3 && (
                                    <span className="px-2 py-0.5 bg-gray-100 text-gray-600 text-xs rounded">
                                      +{video.identified_people_count - 3} more
                                    </span>
                                  )}
                                </div>
                              )}
                            </div>
                          </div>
                        </div>
                        
                        {/* Identified People - shown directly under selected video */}
                        {selectedVideo?.id === video.id && (
                          <div className="mt-2 p-3 bg-gray-50 border border-gray-200 rounded-lg">
                            <h4 className="font-medium text-gray-700 mb-2 text-sm">
                              Identified People ({videoPeople.length})
                            </h4>
                            {videoPeopleLoading ? (
                              <div className="text-sm text-gray-500 text-center py-2">Loading...</div>
                            ) : videoPeople.length === 0 ? (
                              <div className="text-sm text-gray-500 text-center py-2">No people identified yet</div>
                            ) : (
                              <div className="space-y-2 max-h-[300px] overflow-y-auto">
                                {videoPeople.map((person) => (
                                  <div
                                    key={person.person_id}
                                    className="flex items-center justify-between p-2 bg-white rounded border border-gray-200"
                                  >
                                    <div className="flex-1 min-w-0">
                                      <div className="font-medium text-gray-900 text-sm truncate">
                                        {person.first_name} {person.last_name}
                                        {person.middle_name && ` ${person.middle_name}`}
                                        {person.maiden_name && ` (${person.maiden_name})`}
                                      </div>
                                      {person.identified_by && (
                                        <div className="text-xs text-gray-500 truncate">
                                          Identified by {person.identified_by}
                                        </div>
                                      )}
                                    </div>
                                    <button
                                      onClick={() => handleRemovePersonFromVideo(person.person_id)}
                                      className="ml-2 px-2 py-1 text-xs text-red-600 hover:bg-red-50 rounded flex-shrink-0"
                                      title="Remove person"
                                    >
                                      Remove
                                    </button>
                                  </div>
                                ))}
                              </div>
                            )}
                          </div>
                        )}
                      </div>
                    ))}
                  </div>
                )}
                
                {/* Pagination */}
                {videosTotal > 0 && (
                  <div className="mt-4 flex items-center justify-between">
                    <div className="text-sm text-gray-600">
                      Page {videosPage} of {Math.ceil(videosTotal / videosPageSize)}
                    </div>
                    <div className="flex gap-2">
                      <button
                        onClick={() => setVideosPage((p) => Math.max(1, p - 1))}
                        disabled={videosPage === 1}
                        className="px-3 py-1 border border-gray-300 rounded disabled:opacity-50"
                      >
                        Previous
                      </button>
                      <button
                        onClick={() => setVideosPage((p) => p + 1)}
                        disabled={videosPage >= Math.ceil(videosTotal / videosPageSize)}
                        className="px-3 py-1 border border-gray-300 rounded disabled:opacity-50"
                      >
                        Next
                      </button>
                    </div>
                  </div>
                )}
              </div>
            </div>

            {/* Right: Video Details and Identification */}
            <div className="col-span-7">
              {selectedVideo ? (
                <div className="bg-white rounded-lg shadow p-4 space-y-4">
                  <div>
                    <h3 className="text-lg font-semibold text-gray-700 mb-2">
                      {selectedVideo.filename}
                    </h3>
                    <video
                      src={videosApi.getVideoUrl(selectedVideo.id)}
                      controls
                      className="w-full max-h-64 rounded"
                    />
                  </div>

                  {/* Add Person Form */}
                  <div className="border-t pt-4">
                    <h4 className="font-medium text-gray-700 mb-3">Add Person</h4>
                    <div className="space-y-3">
                      <div>
                        <label className="block text-sm font-medium text-gray-700 mb-1">
                          Existing Person
                        </label>
                        <select
                          value={videoPersonId || ''}
                          onChange={(e) => {
                            const val = e.target.value
                            setVideoPersonId(val ? Number(val) : undefined)
                            if (val) {
                              const person = people.find((p) => p.id === Number(val))
                              if (person) {
                                setVideoFirstName('')
                                setVideoLastName('')
                                setVideoMiddleName('')
                                setVideoMaidenName('')
                                setVideoDob('')
                              }
                            }
                          }}
                          className="w-full px-3 py-2 border border-gray-300 rounded-md"
                        >
                          <option value="">-- Select existing person --</option>
                          {people.map((person) => (
                            <option key={person.id} value={person.id}>
                              {person.first_name} {person.last_name}
                              {person.middle_name && ` ${person.middle_name}`}
                              {person.maiden_name && ` (${person.maiden_name})`}
                            </option>
                          ))}
                        </select>
                      </div>
                      <div className="text-sm text-gray-500 text-center">OR</div>
                      <div className="grid grid-cols-2 gap-3">
                        <div>
                          <label className="block text-sm font-medium text-gray-700 mb-1">
                            First Name *
                          </label>
                          <input
                            type="text"
                            value={videoFirstName}
                            onChange={(e) => setVideoFirstName(e.target.value)}
                            disabled={!!videoPersonId}
                            className="w-full px-3 py-2 border border-gray-300 rounded-md disabled:bg-gray-100"
                          />
                        </div>
                        <div>
                          <label className="block text-sm font-medium text-gray-700 mb-1">
                            Last Name *
                          </label>
                          <input
                            type="text"
                            value={videoLastName}
                            onChange={(e) => setVideoLastName(e.target.value)}
                            disabled={!!videoPersonId}
                            className="w-full px-3 py-2 border border-gray-300 rounded-md disabled:bg-gray-100"
                          />
                        </div>
                        <div>
                          <label className="block text-sm font-medium text-gray-700 mb-1">
                            Middle Name
                          </label>
                          <input
                            type="text"
                            value={videoMiddleName}
                            onChange={(e) => setVideoMiddleName(e.target.value)}
                            disabled={!!videoPersonId}
                            className="w-full px-3 py-2 border border-gray-300 rounded-md disabled:bg-gray-100"
                          />
                        </div>
                        <div>
                          <label className="block text-sm font-medium text-gray-700 mb-1">
                            Maiden Name
                          </label>
                          <input
                            type="text"
                            value={videoMaidenName}
                            onChange={(e) => setVideoMaidenName(e.target.value)}
                            disabled={!!videoPersonId}
                            className="w-full px-3 py-2 border border-gray-300 rounded-md disabled:bg-gray-100"
                          />
                        </div>
                        <div className="col-span-2">
                          <label className="block text-sm font-medium text-gray-700 mb-1">
                            Date of Birth
                          </label>
                          <input
                            type="date"
                            value={videoDob}
                            onChange={(e) => setVideoDob(e.target.value)}
                            disabled={!!videoPersonId}
                            className="w-full px-3 py-2 border border-gray-300 rounded-md disabled:bg-gray-100"
                          />
                        </div>
                      </div>
                      <button
                        onClick={handleVideoIdentify}
                        disabled={videoIdentifying || (!videoPersonId && (!videoFirstName.trim() || !videoLastName.trim()))}
                        className="w-full px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 disabled:opacity-50 disabled:cursor-not-allowed"
                      >
                        {videoIdentifying ? 'Adding...' : 'Add Person to Video'}
                      </button>
                    </div>
                  </div>
                </div>
              ) : (
                <div className="bg-white rounded-lg shadow p-8 text-center">
                  <p className="text-gray-500">Select a video to identify people</p>
                </div>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  )
}


