/**
 * Private Gallery Bundle Lightbox
 *
 * Duplicated from paid-content/BundleLightbox.tsx and modified for private gallery.
 * Uses private gallery API endpoints with token authentication.
 */

import { useEffect, useState, useRef, useCallback } from 'react'
import { useGesture } from '@use-gesture/react'
import {
  ChevronLeft,
  ChevronRight,
  MoreHorizontal,
  X,
  Download,
  ZoomIn,
  ZoomOut,
  RotateCcw,
  Video,
  User,
  Info,
  Image as ImageIcon,
  HardDrive,
  Calendar,
  Clock,
  Edit,
  Trash2,
  Play,
  Pause,
  Shuffle,
  PictureInPicture2,
} from 'lucide-react'
import { notificationManager } from '../../lib/notificationManager'
import { api } from '../../lib/api'

// Private Gallery Media Item interface
export interface PrivateGalleryMediaItem {
  id: number
  storage_id: string
  filename: string
  description?: string | null
  file_type: 'image' | 'video' | 'other'
  file_size: number
  width?: number | null
  height?: number | null
  duration?: number | null
  person?: {
    id: number
    name: string
    relationship?: { id: number; name: string; color: string }
  } | null
  media_date: string
  post_date?: string
  tags?: Array<{ id: number; name: string; slug?: string; color: string }>
  thumbnail_url: string
  file_url: string
  stream_url: string
  created_at?: string
}

// Format bytes to human readable
function formatBytes(bytes: number | null | undefined): string {
  if (!bytes || bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`
}

// Format date
function formatDate(dateStr: string | null | undefined): string {
  if (!dateStr) return ''
  // Append T00:00:00 to date-only strings so they parse as local time, not UTC
  const normalized = dateStr.includes('T') ? dateStr : `${dateStr}T00:00:00`
  const date = new Date(normalized)
  if (isNaN(date.getTime())) return dateStr
  return date.toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  })
}

// Format video duration
function formatDuration(seconds: number | null | undefined): string {
  if (!seconds || seconds <= 0) return ''
  const h = Math.floor(seconds / 3600)
  const m = Math.floor((seconds % 3600) / 60)
  const s = Math.floor(seconds % 60)
  if (h > 0) return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
  return `${m}:${s.toString().padStart(2, '0')}`
}

interface PrivateGalleryBundleLightboxProps {
  items: PrivateGalleryMediaItem[]
  currentIndex: number
  onClose: () => void
  onNavigate: (index: number) => void
  onEdit?: (item: PrivateGalleryMediaItem) => void
  onDelete?: (item: PrivateGalleryMediaItem) => void
  initialSlideshow?: boolean
  initialInterval?: number  // Override default slideshow interval (ms)
  initialShuffle?: boolean  // Override default shuffle setting
  totalCount?: number  // Total items across all pages (for position indicator)
  hasMore?: boolean  // Whether more pages can be loaded
  onLoadMore?: () => void  // Fetch next page
  onShuffleChange?: (enabled: boolean) => void  // Parent-managed shuffle (backend random)
  isShuffled?: boolean  // Whether items are already in shuffled order from parent
}

export default function PrivateGalleryBundleLightbox({
  items,
  currentIndex,
  onClose,
  onNavigate,
  onEdit,
  onDelete,
  initialSlideshow = false,
  initialInterval,
  initialShuffle,
  totalCount,
  hasMore,
  onLoadMore,
  onShuffleChange,
  isShuffled,
}: PrivateGalleryBundleLightboxProps) {
  const currentItem = items[currentIndex]
  const isCurrentVideo = currentItem?.file_type === 'video'
  const token = api.getPrivateGalleryToken()

  // Mobile detection (landscape phones get desktop layout)
  const [isMobile, setIsMobile] = useState(false)
  const [isLandscape, setIsLandscape] = useState(false)
  useEffect(() => {
    const check = () => {
      const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0
      const portrait = window.innerWidth < 768
      const landscapePhone = hasTouch && window.innerHeight < 500
      setIsMobile(portrait && !landscapePhone)
      setIsLandscape(landscapePhone && !portrait)
    }
    check()
    window.addEventListener('resize', check)
    return () => window.removeEventListener('resize', check)
  }, [])

  // Bundle sidebar state - collapsed by default, user can expand manually
  const [sidebarOpen, setSidebarOpen] = useState(false)
  const [menuOpen, setMenuOpen] = useState(false)
  const [showMetadata, setShowMetadata] = useState(false)
  const showControls = true // Controls always visible (removed tap-to-toggle for better UX)

  // Slideshow state
  const [slideshowPlaying, setSlideshowPlaying] = useState(initialSlideshow)
  const [slideshowInterval, setSlideshowInterval] = useState(initialInterval ?? 5000)
  const slideshowTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)

  // Shuffle state: shuffleOrder maps sequential positions to actual item indices
  // When parent manages shuffle (onShuffleChange provided), items are already in shuffled order.
  // Internal shuffle state is only used for client-side shuffle (no onShuffleChange).
  const [shuffleEnabled, setShuffleEnabled] = useState(onShuffleChange ? false : (initialShuffle ?? false))
  const [shuffleOrder, setShuffleOrder] = useState<number[]>([])
  const [shufflePosition, setShufflePosition] = useState(0) // current position in shuffled sequence

  const buildShuffleOrder = useCallback((startIndex: number) => {
    const indices = Array.from({ length: items.length }, (_, i) => i)
    // Fisher-Yates shuffle
    for (let i = indices.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [indices[i], indices[j]] = [indices[j], indices[i]]
    }
    // Move startIndex to front so current item stays
    const pos = indices.indexOf(startIndex)
    if (pos > 0) {
      [indices[0], indices[pos]] = [indices[pos], indices[0]]
    }
    setShuffleOrder(indices)
    setShufflePosition(0)
  }, [items.length])

  // When new items are loaded, shuffle the new indices into the remaining unplayed portion
  // Only for client-side shuffle (not parent-managed)
  const prevItemsLengthRef = useRef(items.length)
  useEffect(() => {
    if (!onShuffleChange && shuffleEnabled && items.length > prevItemsLengthRef.current && shuffleOrder.length > 0) {
      const oldLen = prevItemsLengthRef.current
      const newIndices = Array.from({ length: items.length - oldLen }, (_, i) => i + oldLen)
      // Shuffle new indices
      for (let i = newIndices.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [newIndices[i], newIndices[j]] = [newIndices[j], newIndices[i]]
      }
      // Insert new indices randomly into the unplayed portion (after shufflePosition)
      const played = shuffleOrder.slice(0, shufflePosition + 1)
      const remaining = shuffleOrder.slice(shufflePosition + 1)
      // Merge remaining and new indices with a fresh shuffle
      const merged = [...remaining, ...newIndices]
      for (let i = merged.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [merged[i], merged[j]] = [merged[j], merged[i]]
      }
      setShuffleOrder([...played, ...merged])
    }
    prevItemsLengthRef.current = items.length
  }, [items.length, shuffleEnabled, shuffleOrder, shufflePosition])

  const toggleShuffle = useCallback(() => {
    if (onShuffleChange) {
      // Parent-managed shuffle: delegate to parent, skip internal shuffle
      onShuffleChange(!isShuffled)
      return
    }
    if (!shuffleEnabled) {
      buildShuffleOrder(currentIndex)
      setShuffleEnabled(true)
    } else {
      setShuffleEnabled(false)
      setShuffleOrder([])
      setShufflePosition(0)
    }
  }, [shuffleEnabled, currentIndex, buildShuffleOrder, onShuffleChange, isShuffled])

  // Build shuffle order on mount when starting with shuffle enabled
  useEffect(() => {
    if (shuffleEnabled && shuffleOrder.length === 0 && !onShuffleChange) {
      buildShuffleOrder(currentIndex)
    }
  }, []) // eslint-disable-line react-hooks/exhaustive-deps

  // Zoom/pan state for images
  const [zoom, setZoom] = useState(1)
  const [pan, setPan] = useState({ x: 0, y: 0 })
  const [isPanning, setIsPanning] = useState(false)
  const [panStart, setPanStart] = useState({ x: 0, y: 0 })

  // Loading states
  const [imageLoading, setImageLoading] = useState(true)
  const [imageError, setImageError] = useState(false)
  const loadedUrlRef = useRef<string | null>(null)

  // Video bottom position for dynamic bar placement
  const [, setVideoBottomOffset] = useState(67) // pixels from bottom (mobile default)

  // Video aspect ratio for portrait video sizing
  const [videoAspectRatio, setVideoAspectRatio] = useState<number | null>(null)

  // Image dimensions for zoom calculation
  const [naturalSize, setNaturalSize] = useState<{ width: number; height: number } | null>(null)
  const [displayedSize, setDisplayedSize] = useState<{ width: number; height: number } | null>(null)

  // Auto-hide floating zoom controls
  const [zoomControlsVisible, setZoomControlsVisible] = useState(false)
  const zoomControlsTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)

  const maxZoom = naturalSize && displayedSize
    ? Math.max(5, Math.ceil((naturalSize.width / displayedSize.width) * 1.5))
    : 5

  const containerRef = useRef<HTMLDivElement>(null)
  const imageRef = useRef<HTMLImageElement>(null)
  const thumbnailStripRef = useRef<HTMLDivElement>(null)
  const videoRef = useRef<HTMLVideoElement>(null)

  // Handle closing
  const handleClose = useCallback(() => {
    onClose()
  }, [onClose])

  // Show zoom controls with auto-hide timer
  const showZoomControls = useCallback(() => {
    if (isCurrentVideo) return
    setZoomControlsVisible(true)
    if (zoomControlsTimerRef.current) clearTimeout(zoomControlsTimerRef.current)
    zoomControlsTimerRef.current = setTimeout(() => setZoomControlsVisible(false), 2500)
  }, [isCurrentVideo])

  // Cleanup zoom controls timer on unmount
  useEffect(() => {
    return () => { if (zoomControlsTimerRef.current) clearTimeout(zoomControlsTimerRef.current) }
  }, [])

  // Set video bottom offset (simplified - use fixed offset for HLS player)
  const calculateVideoBottomOffset = useCallback(() => {
    // Use a fixed offset that works well for most video aspect ratios
    // Position post info bar above the video controls
    // Mobile: 67 (27px from bottom), Desktop: 120
    setVideoBottomOffset(isMobile ? 67 : isLandscape ? 50 : 120)
  }, [isMobile, isLandscape])

  // Slideshow: collapse sidebar and hide metadata when starting
  useEffect(() => {
    if (slideshowPlaying) {
      setSidebarOpen(false)
      setShowMetadata(false)
    }
  }, [slideshowPlaying])

  // Preload next page when approaching end of loaded items
  useEffect(() => {
    if (hasMore && onLoadMore && currentIndex >= items.length - 5) {
      onLoadMore()
    }
  }, [currentIndex, items.length, hasMore, onLoadMore])

  // Slideshow: advance to next item (respects shuffle order)
  const slideshowAdvance = useCallback(() => {
    if (shuffleEnabled && shuffleOrder.length > 0) {
      const nextPos = shufflePosition + 1
      if (nextPos < shuffleOrder.length) {
        setShufflePosition(nextPos)
        onNavigate(shuffleOrder[nextPos])
      } else {
        // If more items can be loaded, wait; otherwise stop
        if (hasMore && onLoadMore) {
          onLoadMore()
        } else {
          setSlideshowPlaying(false)
        }
      }
    } else {
      if (currentIndex < items.length - 1) {
        onNavigate(currentIndex + 1)
      } else if (hasMore && onLoadMore) {
        // More pages available - fetch and wait for items to grow
        onLoadMore()
      } else {
        setSlideshowPlaying(false)
      }
    }
  }, [shuffleEnabled, shuffleOrder, shufflePosition, currentIndex, items.length, onNavigate, hasMore, onLoadMore])

  // Slideshow timer logic for images — wait for load, then start countdown
  useEffect(() => {
    if (slideshowTimerRef.current) {
      clearTimeout(slideshowTimerRef.current)
      slideshowTimerRef.current = null
    }

    if (slideshowPlaying && !isCurrentVideo && !imageLoading) {
      slideshowTimerRef.current = setTimeout(() => {
        slideshowAdvance()
      }, slideshowInterval)
    }

    return () => {
      if (slideshowTimerRef.current) {
        clearTimeout(slideshowTimerRef.current)
        slideshowTimerRef.current = null
      }
    }
  }, [slideshowPlaying, isCurrentVideo, imageLoading, slideshowInterval, slideshowAdvance])

  // Preload the next image during slideshow — only after current image has loaded
  // to avoid competing for the single API worker's decryption thread
  useEffect(() => {
    if (!slideshowPlaying || imageLoading) return
    let nextIndex: number | null = null
    if (shuffleEnabled && shuffleOrder.length > 0) {
      const nextPos = shufflePosition + 1
      if (nextPos < shuffleOrder.length) nextIndex = shuffleOrder[nextPos]
    } else {
      if (currentIndex < items.length - 1) nextIndex = currentIndex + 1
    }
    if (nextIndex !== null && nextIndex < items.length) {
      const nextItem = items[nextIndex]
      if (nextItem && nextItem.file_type !== 'video') {
        const img = new Image()
        img.src = getPreviewUrl(nextItem)
      }
    }
  }, [slideshowPlaying, imageLoading, currentIndex, shuffleEnabled, shuffleOrder, shufflePosition, items])

  // Slideshow: handle video ended event to advance
  useEffect(() => {
    if (!slideshowPlaying || !isCurrentVideo) return

    const video = videoRef.current
    if (!video) return

    const handleEnded = () => {
      slideshowAdvance()
    }

    video.addEventListener('ended', handleEnded)
    return () => video.removeEventListener('ended', handleEnded)
  }, [slideshowPlaying, isCurrentVideo, slideshowAdvance])

  // Auto-hide sidebar for videos
  useEffect(() => {
    if (isCurrentVideo) {
      setSidebarOpen(false)
    }
  }, [isCurrentVideo])

  // Update video bottom offset when mobile state changes
  useEffect(() => {
    if (isCurrentVideo) {
      setVideoBottomOffset(isMobile ? 67 : isLandscape ? 50 : 120)
    }
  }, [isMobile, isLandscape, isCurrentVideo])

  // Shuffle-aware prev/next navigation
  const canGoPrev = shuffleEnabled ? shufflePosition > 0 : currentIndex > 0
  const canGoNext = shuffleEnabled
    ? shufflePosition < shuffleOrder.length - 1 || !!hasMore
    : currentIndex < items.length - 1 || !!hasMore


  const navigatePrev = useCallback(() => {
    if (shuffleEnabled && shuffleOrder.length > 0) {
      if (shufflePosition > 0) {
        const prevPos = shufflePosition - 1
        setShufflePosition(prevPos)
        onNavigate(shuffleOrder[prevPos])
      }
    } else {
      if (currentIndex > 0) onNavigate(currentIndex - 1)
    }
  }, [shuffleEnabled, shuffleOrder, shufflePosition, currentIndex, onNavigate])

  const navigateNext = useCallback(() => {
    if (shuffleEnabled && shuffleOrder.length > 0) {
      const nextPos = shufflePosition + 1
      if (nextPos < shuffleOrder.length) {
        setShufflePosition(nextPos)
        onNavigate(shuffleOrder[nextPos])
      }
    } else {
      if (currentIndex < items.length - 1) onNavigate(currentIndex + 1)
    }
  }, [shuffleEnabled, shuffleOrder, shufflePosition, currentIndex, items.length, onNavigate])

  // Keyboard shortcuts
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return

      switch (e.key) {
        case ' ':
          e.preventDefault()
          setSlideshowPlaying(p => !p)
          break
        case 'ArrowLeft':
          e.preventDefault()
          navigatePrev()
          break
        case 'ArrowRight':
          e.preventDefault()
          navigateNext()
          break
        case 'Escape':
          e.preventDefault()
          if (slideshowPlaying) {
            setSlideshowPlaying(false)
          } else {
            handleClose()
          }
          break
        case '+':
        case '=':
          e.preventDefault()
          if (!isCurrentVideo) { setZoom(z => Math.min(z + 0.5, maxZoom)); showZoomControls() }
          break
        case '-':
        case '_':
          e.preventDefault()
          if (!isCurrentVideo) { setZoom(z => Math.max(z - 0.5, 1)); showZoomControls() }
          break
        case '0':
          e.preventDefault()
          setZoom(1)
          setPan({ x: 0, y: 0 })
          showZoomControls()
          break
        case 'i':
        case 'I':
          e.preventDefault()
          setShowMetadata(s => !s)
          break
        case 'Delete':
        case 'Backspace':
          e.preventDefault()
          if (onDelete) onDelete(items[currentIndex])
          break
      }
    }

    window.addEventListener('keydown', handleKeyDown)
    return () => window.removeEventListener('keydown', handleKeyDown)
  }, [navigatePrev, navigateNext, handleClose, isCurrentVideo, maxZoom, slideshowPlaying, onDelete, items, currentIndex, showZoomControls])

  // Reset zoom/pan when changing items
  useEffect(() => {
    setZoom(1)
    setPan({ x: 0, y: 0 })
    setImageError(false)
    setNaturalSize(null)
    setDisplayedSize(null)
    setMenuOpen(false)
    setVideoAspectRatio(null)
    // Only set loading=true if the image hasn't already loaded (e.g. from cache).
    // onLoad sets loadedUrlRef before this effect runs, so check it.
    const url = currentItem ? getPreviewUrl(currentItem) : null
    if (loadedUrlRef.current !== url) {
      setImageLoading(true)
    }
  }, [currentIndex])

  // Mouse wheel zoom
  const handleWheel = useCallback((e: WheelEvent) => {
    if (isCurrentVideo) return
    if (e.ctrlKey || e.metaKey) {
      e.preventDefault()
      const delta = e.deltaY > 0 ? -0.2 : 0.2
      setZoom(z => Math.max(1, Math.min(maxZoom, z + delta)))
      showZoomControls()
    }
  }, [maxZoom, isCurrentVideo, showZoomControls])

  useEffect(() => {
    const container = containerRef.current
    if (container) {
      container.addEventListener('wheel', handleWheel, { passive: false })
      return () => container.removeEventListener('wheel', handleWheel)
    }
  }, [handleWheel])

  // Pan handling for images
  const handleMouseDown = (e: React.MouseEvent) => {
    if (zoom > 1 && !isCurrentVideo) {
      setIsPanning(true)
      setPanStart({ x: e.clientX - pan.x, y: e.clientY - pan.y })
    }
  }

  const handleMouseMove = (e: React.MouseEvent) => {
    showZoomControls()
    if (isPanning) {
      setPan({
        x: e.clientX - panStart.x,
        y: e.clientY - panStart.y,
      })
    }
  }

  const handleMouseUp = () => {
    setIsPanning(false)
  }

  // Gesture refs for touch support
  const gestureZoomRef = useRef(zoom)
  const gesturePanRef = useRef(pan)

  useEffect(() => {
    gestureZoomRef.current = zoom
    gesturePanRef.current = pan
  }, [zoom, pan])

  // Track swipe state for navigation
  const swipeStartRef = useRef<{ x: number; y: number; time: number } | null>(null)

  // Touch gesture handling for images (pinch zoom, drag pan)
  useGesture(
    {
      onPinch: ({ offset: [scale], last }) => {
        if (isCurrentVideo) return
        const newZoom = Math.max(1, Math.min(maxZoom, scale))
        gestureZoomRef.current = newZoom

        if (imageRef.current) {
          const p = gesturePanRef.current
          imageRef.current.style.transform = `scale3d(${newZoom}, ${newZoom}, 1) translate3d(${p.x / newZoom}px, ${p.y / newZoom}px, 0)`
        }

        if (last) setZoom(newZoom)
      },
      onDrag: ({ offset: [x, y], pinching, last }) => {
        if (isCurrentVideo || pinching) return
        const currentZoom = gestureZoomRef.current
        if (currentZoom <= 1) return

        gesturePanRef.current = { x, y }

        if (imageRef.current) {
          imageRef.current.style.transform = `scale3d(${currentZoom}, ${currentZoom}, 1) translate3d(${x / currentZoom}px, ${y / currentZoom}px, 0)`
        }

        if (last) setPan({ x, y })
      },
    },
    {
      target: imageRef,
      eventOptions: { passive: false },
      pinch: { scaleBounds: { min: 1, max: maxZoom }, rubberband: true },
      drag: { from: () => [gesturePanRef.current.x, gesturePanRef.current.y] },
    }
  )

  // Swipe navigation for mobile (works for both images and videos)
  const handleTouchStart = useCallback((e: React.TouchEvent) => {
    showZoomControls()
    // Don't track swipes that start on interactive elements
    const target = e.target as HTMLElement
    if (target.closest('button, a, video, input, textarea')) {
      swipeStartRef.current = null
      return
    }

    if (e.touches.length === 1) {
      swipeStartRef.current = {
        x: e.touches[0].clientX,
        y: e.touches[0].clientY,
        time: Date.now(),
      }
    }
  }, [showZoomControls])

  const handleTouchEnd = useCallback((e: React.TouchEvent) => {
    if (!swipeStartRef.current || e.changedTouches.length !== 1) return

    const start = swipeStartRef.current
    const end = {
      x: e.changedTouches[0].clientX,
      y: e.changedTouches[0].clientY,
    }

    const deltaX = end.x - start.x
    const deltaY = end.y - start.y
    const deltaTime = Date.now() - start.time

    // Must be a quick swipe (< 300ms) with significant horizontal movement
    const minSwipeDistance = 50
    const maxVerticalDeviation = 100

    if (deltaTime < 300 && Math.abs(deltaX) > minSwipeDistance && Math.abs(deltaY) < maxVerticalDeviation) {
      // Only navigate if not zoomed in (for images)
      if (!isCurrentVideo && zoom > 1) {
        swipeStartRef.current = null
        return
      }

      if (deltaX > 0) {
        // Swipe right = previous
        navigatePrev()
      } else if (deltaX < 0) {
        // Swipe left = next
        navigateNext()
      }
    }
    // Note: Don't toggle controls on tap here - let click events handle buttons naturally

    swipeStartRef.current = null
  }, [navigatePrev, navigateNext, isCurrentVideo, zoom])

  // URL helpers with token
  const getPreviewUrl = (item: PrivateGalleryMediaItem) => {
    const baseUrl = item.file_type === 'video' ? item.stream_url : item.file_url
    return token ? `${baseUrl}?_token=${encodeURIComponent(token)}` : baseUrl
  }

  const getThumbnailUrl = (item: PrivateGalleryMediaItem) => {
    return token ? `${item.thumbnail_url}?_token=${encodeURIComponent(token)}` : item.thumbnail_url
  }

  // Actions
  const handleDownload = async () => {
    try {
      const url = currentItem.file_url + (token ? `?_token=${encodeURIComponent(token)}` : '')
      const filename = currentItem.filename || `file_${currentItem.id}`
      const response = await fetch(url)
      const blob = await response.blob()
      const downloadUrl = window.URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = downloadUrl
      a.download = filename
      document.body.appendChild(a)
      a.click()
      document.body.removeChild(a)
      window.URL.revokeObjectURL(downloadUrl)
      notificationManager.success('Downloaded', filename)
      setMenuOpen(false)
    } catch {
      notificationManager.error('Download Failed', 'Could not download file')
    }
  }

  const handlePiP = async () => {
    const video = videoRef.current
    if (!video) return
    setMenuOpen(false)
    try {
      if (document.pictureInPictureEnabled && video.requestPictureInPicture) {
        await video.requestPictureInPicture()
        return
      }
      if ((video as any).webkitSupportsPresentationMode?.('picture-in-picture')) {
        ;(video as any).webkitSetPresentationMode('picture-in-picture')
        return
      }
      notificationManager.error('PiP unavailable', 'Picture-in-Picture is not supported on this device')
    } catch {
      notificationManager.error('PiP failed', 'Could not enter Picture-in-Picture mode')
    }
  }

  // Scroll thumbnail into view
  useEffect(() => {
    if (thumbnailStripRef.current) {
      const thumb = thumbnailStripRef.current.children[currentIndex] as HTMLElement
      if (thumb) {
        thumb.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
      }
    }
  }, [currentIndex])

  return (
    <div
      ref={containerRef}
      className="fixed inset-0 z-50 bg-black flex"
      style={isLandscape ? { height: '100dvh', width: '100vw' } : undefined}
      onClick={(e) => e.stopPropagation()}
      onMouseMove={handleMouseMove}
      onMouseUp={handleMouseUp}
      onMouseLeave={handleMouseUp}
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
    >
      {/* ===== BUNDLE SIDEBAR (Images only, hidden on mobile) ===== */}
      {!isCurrentVideo && !isMobile && sidebarOpen && items.length > 1 && (
        <div className="w-24 bg-black flex flex-col flex-shrink-0 border-r border-white/10 pt-8 pl-3 relative z-20">
          {/* Sidebar Header */}
          <div className="p-2 flex items-center justify-between">
            <span className="text-white text-xs font-medium">Bundle</span>
            <button
              onClick={() => setSidebarOpen(false)}
              className="p-1 hover:bg-white/10 rounded text-white/60 hover:text-white transition-colors"
              title="Collapse"
            >
              <ChevronLeft className="w-4 h-4" />
            </button>
          </div>

          {/* Thumbnail Strip */}
          <div ref={thumbnailStripRef} className="flex-1 overflow-y-auto space-y-3 px-1 py-1">
            {items.map((item, idx) => {
              const isVideo = item.file_type === 'video'
              const isCurrent = idx === currentIndex
              return (
                <button
                  key={item.id}
                  onClick={() => onNavigate(idx)}
                  className={`relative w-full aspect-square rounded overflow-hidden transition-all ${
                    isCurrent
                      ? 'ring-2 ring-white ring-offset-2 ring-offset-black'
                      : 'opacity-40 hover:opacity-80'
                  }`}
                >
                  <img
                    src={getThumbnailUrl(item)}
                    alt=""
                    className="w-full h-full object-cover"
                    loading="lazy"
                  />
                  {isVideo && (
                    <div className="absolute inset-0 flex items-center justify-center bg-black/40">
                      <Video className="w-4 h-4 text-white" />
                    </div>
                  )}
                </button>
              )
            })}
          </div>
        </div>
      )}
      {/* Bundle sidebar expand button (when sidebar is closed, desktop images only) */}
      {!isCurrentVideo && !isMobile && !sidebarOpen && items.length > 1 && (
        <button
          onClick={() => setSidebarOpen(true)}
          className="absolute top-10 left-2 z-20 p-1.5 bg-black/50 hover:bg-black/70 rounded-lg text-white/60 hover:text-white transition-colors"
          title="Show Bundle"
        >
          <ChevronRight className="w-4 h-4" />
        </button>
      )}

      {/* ===== MAIN CONTENT AREA ===== */}
      <div className="flex-1 flex flex-col relative" style={isLandscape ? { height: '100dvh' } : undefined}>
        {/* Top Left: Slideshow Controls */}
        <div
          className={`absolute z-20 flex items-center gap-2 transition-opacity duration-200 ${
            isMobile ? `top-0 ${items.length > 1 ? 'left-12' : 'left-4'}` : isLandscape ? 'top-2 left-3' : `top-10 ${sidebarOpen ? 'left-6' : 'left-[7.5rem]'}`
          }`}
          style={isMobile ? { paddingTop: 'env(safe-area-inset-top, 0px)' } : undefined}
        >
          {(totalCount || items.length) > 1 && (
            <button
              onClick={() => setSlideshowPlaying(p => !p)}
              className="p-2 bg-black/50 hover:bg-black/70 rounded-lg text-white transition-colors"
              title={slideshowPlaying ? 'Pause Slideshow (Space)' : 'Play Slideshow (Space)'}
            >
              {slideshowPlaying ? (
                <Pause className="w-5 h-5" />
              ) : (
                <Play className="w-5 h-5" />
              )}
            </button>
          )}
          {slideshowPlaying && (totalCount || items.length) > 1 && (
            <button
              onClick={toggleShuffle}
              className={`p-2 bg-black/50 hover:bg-black/70 rounded-lg transition-colors ${
                (onShuffleChange ? isShuffled : shuffleEnabled) ? 'text-cyan-400' : 'text-white'
              }`}
              title={(onShuffleChange ? isShuffled : shuffleEnabled) ? 'Disable Shuffle' : 'Enable Shuffle'}
            >
              <Shuffle className="w-5 h-5" />
            </button>
          )}
          {slideshowPlaying && (totalCount || items.length) > 1 && (
            <div className="flex items-center gap-1 bg-black/50 rounded-lg p-1">
              {[3000, 5000, 8000, 10000].map(ms => (
                <button
                  key={ms}
                  onClick={() => setSlideshowInterval(ms)}
                  className={`px-2 py-1 rounded text-xs transition-colors ${
                    slideshowInterval === ms
                      ? 'bg-white/30 text-white font-medium'
                      : 'text-white/60 hover:text-white hover:bg-white/10'
                  }`}
                >
                  {ms / 1000}s
                </button>
              ))}
            </div>
          )}
          {/* Position indicator on desktop */}
          {!isMobile && items.length > 1 && (
            <span className="text-white/60 text-sm ml-1">
              {(onShuffleChange ? isShuffled : shuffleEnabled)
                ? `${onShuffleChange ? currentIndex + 1 : shufflePosition + 1} / ${totalCount || items.length} (shuffled)`
                : `${currentIndex + 1} / ${totalCount || items.length}`
              }
              {!totalCount && hasMore && '+'}
            </span>
          )}
        </div>

        {/* Top Right: Info, Menu and Close */}
        <div
          className={`absolute z-20 flex items-center gap-2 transition-opacity duration-200 ${
            isMobile ? 'top-0 right-4 safe-area-inset-top' : isLandscape ? 'top-2 right-3' : 'top-10 right-6'
          } ${isMobile && !showControls ? 'opacity-0 pointer-events-none' : 'opacity-100'}`}
          style={isMobile ? { paddingTop: 'env(safe-area-inset-top, 0px)' } : undefined}
        >
          {/* Info toggle button - hidden until hover on desktop, always visible on mobile when controls shown */}
          <button
            onClick={() => setShowMetadata(!showMetadata)}
            className={`p-2 md:p-2 hover:bg-white/10 rounded-lg transition-colors drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)] ${
              showMetadata ? 'text-cyan-400' : isMobile || isLandscape ? 'text-white/70' : 'text-transparent hover:text-white'
            }`}
            title="Toggle Details (I)"
          >
            <Info className="w-7 h-7 md:w-7 md:h-7 drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)]" />
          </button>
          {/* Delete button - visible icon for quick access */}
          {onDelete && (
            <button
              onClick={() => onDelete(currentItem)}
              className={`p-2 hover:bg-red-500/30 rounded-lg transition-colors drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)] ${
                isMobile || isLandscape ? 'text-red-400/70' : 'text-transparent hover:text-red-400'
              }`}
              title="Delete (Del)"
            >
              <Trash2 className="w-7 h-7 drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)]" />
            </button>
          )}
          <div className="relative">
            <button
              onClick={() => setMenuOpen(!menuOpen)}
              className="p-2 hover:bg-white/10 rounded-lg text-white transition-colors drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)]"
            >
              <MoreHorizontal className="w-7 h-7 drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)]" />
            </button>
            {menuOpen && (
              <div className="absolute top-full right-0 mt-1 w-44 bg-zinc-900 border border-white/10 rounded-lg shadow-xl overflow-hidden">
                <button
                  onClick={handleDownload}
                  className="w-full px-4 py-2.5 text-left text-white text-sm hover:bg-white/10 flex items-center gap-3"
                >
                  <Download className="w-4 h-4" />
                  Download
                </button>
                {onEdit && (
                  <button
                    onClick={() => { onEdit(currentItem); setMenuOpen(false) }}
                    className="w-full px-4 py-2.5 text-left text-white text-sm hover:bg-white/10 flex items-center gap-3"
                  >
                    <Edit className="w-4 h-4" />
                    Edit
                  </button>
                )}
                {onDelete && (
                  <button
                    onClick={() => { onDelete(currentItem); setMenuOpen(false) }}
                    className="w-full px-4 py-2.5 text-left text-red-400 text-sm hover:bg-white/10 flex items-center gap-3"
                  >
                    <Trash2 className="w-4 h-4" />
                    Delete
                  </button>
                )}
                {isCurrentVideo && isMobile && (
                  <>
                    <div className="border-t border-white/10" />
                    <button
                      onClick={handlePiP}
                      className="w-full px-4 py-2.5 text-left text-white text-sm hover:bg-white/10 flex items-center gap-3"
                    >
                      <PictureInPicture2 className="w-4 h-4" />
                      Picture-in-Picture
                    </button>
                  </>
                )}
              </div>
            )}
          </div>
          {/* Close button - always visible on mobile/landscape, hidden until hover on desktop */}
          <button
            onClick={(e) => { e.stopPropagation(); handleClose(); }}
            onTouchEnd={(e) => { e.stopPropagation(); }}
            className={`p-2 hover:bg-white/10 rounded-lg transition-colors drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)] ${
              isMobile || isLandscape ? 'text-white/70' : 'text-transparent hover:text-white'
            }`}
          >
            <X className="w-7 h-7 drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)]" />
          </button>
        </div>

        {/* Mobile position indicator */}
        {isMobile && items.length > 1 && (
          <div
            className={`absolute top-12 left-1/2 -translate-x-1/2 z-10 px-3 py-1 bg-black/50 rounded-full text-white text-sm transition-opacity duration-200 ${
              showControls ? 'opacity-100' : 'opacity-0'
            }`}
            style={{ paddingTop: 'env(safe-area-inset-top, 0px)' }}
          >
            {shuffleEnabled ? shufflePosition + 1 : currentIndex + 1} / {totalCount || items.length}{!totalCount && hasMore && '+'}
          </div>
        )}

        {/* Navigation Arrow - Previous */}
        {canGoPrev && (
          <button
            onClick={navigatePrev}
            className={`absolute top-1/2 -translate-y-1/2 z-20 transition-colors drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)] ${
              isMobile
                ? 'left-2 p-3 bg-black/40 rounded-full text-white/80 active:bg-white/20'
                : isLandscape
                  ? 'left-1 p-3 bg-black/40 rounded-full text-white/80 active:bg-white/20'
                  : 'left-0 px-6 py-16 hover:bg-white/5 rounded-r-2xl text-transparent hover:text-white'
            }`}
          >
            <ChevronLeft className={`${isMobile ? 'w-8 h-8' : isLandscape ? 'w-7 h-7' : 'w-6 h-6'} drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)]`} />
          </button>
        )}

        {/* Navigation Arrow - Next */}
        {canGoNext && (
          <button
            onClick={navigateNext}
            className={`absolute top-1/2 -translate-y-1/2 z-20 transition-colors drop-shadow-[0_2px_4px_rgba(0,0,0,0.8)] ${
              isMobile
                ? 'right-2 p-3 bg-black/40 rounded-full text-white/80 active:bg-white/20'
                : isLandscape
                  ? 'right-1 p-3 bg-black/40 rounded-full text-white/80 active:bg-white/20'
                  : 'right-0 px-6 py-16 hover:bg-white/5 rounded-l-2xl text-transparent hover:text-white'
            }`}
          >
            <ChevronRight className={`${isMobile ? 'w-8 h-8' : isLandscape ? 'w-7 h-7' : 'w-6 h-6'} drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)]`} />
          </button>
        )}

        {/* Floating Zoom Controls Pill */}
        {!isCurrentVideo && (
          <div
            className={`absolute ${isMobile ? 'bottom-24' : 'bottom-8'} left-1/2 -translate-x-1/2 z-30
              flex items-center gap-1 px-2 py-1.5 bg-black/70 backdrop-blur-sm rounded-full
              transition-opacity duration-300
              ${zoomControlsVisible ? 'opacity-100' : 'opacity-0 pointer-events-none'}`}
            onMouseEnter={() => {
              if (zoomControlsTimerRef.current) clearTimeout(zoomControlsTimerRef.current)
              setZoomControlsVisible(true)
            }}
            onMouseLeave={() => {
              zoomControlsTimerRef.current = setTimeout(() => setZoomControlsVisible(false), 2500)
            }}
          >
            <button onClick={() => { setZoom(z => Math.max(1, z - 0.5)); showZoomControls() }}
              disabled={zoom <= 1}
              className="p-1.5 text-white/80 hover:text-white disabled:text-white/30" title="Zoom out (-)">
              <ZoomOut className="w-4 h-4" />
            </button>
            <span className="text-white/90 text-xs font-mono min-w-[3rem] text-center select-none">
              {naturalSize && displayedSize
                ? `${Math.round((displayedSize.width / naturalSize.width) * zoom * 100)}%`
                : `${Math.round(zoom * 100)}%`}
            </span>
            <button onClick={() => { setZoom(z => Math.min(maxZoom, z + 0.5)); showZoomControls() }}
              disabled={zoom >= maxZoom}
              className="p-1.5 text-white/80 hover:text-white disabled:text-white/30" title="Zoom in (+)">
              <ZoomIn className="w-4 h-4" />
            </button>
            {naturalSize && displayedSize && (
              <button
                onClick={() => { setZoom(naturalSize.width / displayedSize.width); showZoomControls() }}
                disabled={Math.abs(zoom - naturalSize.width / displayedSize.width) < 0.01}
                className="px-1.5 py-0.5 text-white/80 hover:text-white disabled:text-white/30 text-xs font-medium"
                title="Actual size (1)">
                100%
              </button>
            )}
            <button onClick={() => { setZoom(1); setPan({ x: 0, y: 0 }); showZoomControls() }}
              disabled={zoom === 1 && pan.x === 0 && pan.y === 0}
              className="p-1.5 text-white/80 hover:text-white disabled:text-white/30" title="Reset view (0)">
              <RotateCcw className="w-4 h-4" />
            </button>
          </div>
        )}

        {/* Media Display Area */}
        <div
          className={`flex-1 flex items-center justify-center ${isCurrentVideo ? '' : isMobile ? 'px-2 pb-16 pt-2' : isLandscape ? 'px-2 pb-2 pt-1' : 'px-16 pb-20 pt-4'}`}
          onClick={() => menuOpen && setMenuOpen(false)}
        >
          {isCurrentVideo ? (
            /* ===== FULLSCREEN VIDEO PLAYER ===== */
            <div className={`fixed z-10 inset-0 flex ${isMobile ? 'items-center pb-[72px]' : videoAspectRatio && videoAspectRatio >= 1 ? 'items-center pt-[30px]' : 'items-start'} justify-center bg-black`}>
              {/* Blurred background for mobile (all videos) and portrait videos on desktop */}
              <div
                className="absolute inset-0 scale-150"
                style={{
                  backgroundImage: `url(${getThumbnailUrl(currentItem)})`,
                  backgroundSize: 'cover',
                  backgroundPosition: 'center',
                  filter: 'blur(30px) brightness(0.75)',
                }}
              />
              <video
                ref={videoRef}
                key={currentItem.id}
                src={getPreviewUrl(currentItem)}
                className={`object-contain relative z-10 ${
                  // Mobile: fill width for all videos (controls follow video)
                  isMobile ? 'w-full' :
                  // Landscape phone: size to fit viewport using max dimensions
                  isLandscape ? 'max-w-full max-h-full' :
                  // Desktop portrait: fill height, constrain width
                  videoAspectRatio && videoAspectRatio < 1 ? 'h-full' :
                  // Desktop landscape: fill width, constrain to container
                  'w-full max-h-full'
                }`}
                style={isMobile && videoAspectRatio
                  ? { aspectRatio: videoAspectRatio }
                  : isLandscape && videoAspectRatio
                    ? { aspectRatio: videoAspectRatio, height: '100%' }
                    : videoAspectRatio && videoAspectRatio < 1
                      ? { aspectRatio: videoAspectRatio }
                      : undefined
                }
                // @ts-ignore - AirPlay attributes
                x-webkit-airplay="allow"
                airplay="allow"
                controls
                autoPlay
                muted={isMobile}
                playsInline
                preload="auto"
                // @ts-ignore - PIP support
                disablePictureInPicture={false}
                onLoadStart={() => setImageLoading(true)}
                onLoadedData={(e) => {
                  setImageLoading(false)
                  const video = e.currentTarget
                  // Autoplay on mobile: start muted for autoplay policy, then unmute
                  if (video.paused) {
                    video.play().then(() => {
                      if (isMobile && video.muted) {
                        video.muted = false
                      }
                    }).catch(() => {})
                  } else if (isMobile && !video.paused && video.muted) {
                    video.muted = false
                  }
                  // Detect video aspect ratio for portrait videos
                  if (video.videoWidth && video.videoHeight) {
                    setVideoAspectRatio(video.videoWidth / video.videoHeight)
                  }
                  calculateVideoBottomOffset()
                }}
                onCanPlay={() => setImageLoading(false)}
              />
            </div>
          ) : (
            /* ===== FULLSCREEN IMAGE DISPLAY ===== */
            <div
              className="fixed inset-0 flex items-center justify-center bg-black z-10"
              onMouseDown={handleMouseDown}
              style={{
                cursor: zoom > 1 ? 'grab' : 'default',
              }}
            >
              {imageLoading && (
                <div className="absolute inset-0 flex items-center justify-center">
                  <div className="w-10 h-10 border-4 border-white/20 border-t-white rounded-full animate-spin" />
                </div>
              )}
              {imageError ? (
                <div className="text-white text-center p-8">
                  <p className="mb-4">Failed to load image</p>
                  <button
                    onClick={() => { setImageError(false); loadedUrlRef.current = null; setImageLoading(true) }}
                    className="px-4 py-2 bg-white/20 hover:bg-white/30 rounded-lg transition-colors"
                  >
                    Retry
                  </button>
                </div>
              ) : (
                <img
                  ref={imageRef}
                  key={getPreviewUrl(currentItem)}
                  src={getPreviewUrl(currentItem)}
                  alt={currentItem.filename || ''}
                  className="w-full h-full object-contain select-none"
                  style={{
                    transform: `scale3d(${zoom}, ${zoom}, 1) translate3d(${pan.x / zoom}px, ${pan.y / zoom}px, 0)`,
                    transformOrigin: 'center',
                    opacity: imageLoading ? 0 : 1,
                    transition: 'opacity 0.2s',
                    touchAction: 'none',
                  }}
                  onLoad={(e) => {
                    loadedUrlRef.current = getPreviewUrl(currentItem)
                    setImageLoading(false)
                    const img = e.target as HTMLImageElement
                    setNaturalSize({ width: img.naturalWidth, height: img.naturalHeight })
                    // Calculate actual rendered size accounting for object-contain
                    // clientWidth/clientHeight report the CSS box, not the visible image
                    const containerAspect = img.clientWidth / img.clientHeight
                    const imageAspect = img.naturalWidth / img.naturalHeight
                    if (imageAspect > containerAspect) {
                      setDisplayedSize({ width: img.clientWidth, height: img.clientWidth / imageAspect })
                    } else {
                      setDisplayedSize({ width: img.clientHeight * imageAspect, height: img.clientHeight })
                    }
                  }}
                  onError={() => { setImageLoading(false); setImageError(true) }}
                  draggable={false}
                />
              )}
            </div>
          )}
        </div>

        {/* ===== METADATA PANEL ===== */}
        <div
          className={`absolute transition-all duration-300 ease-in-out z-30 ${
            isMobile
              ? `inset-x-0 bottom-0 top-[62px] ${showMetadata ? 'opacity-100' : 'opacity-0 pointer-events-none'}`
              : `right-6 bottom-16 w-80 ${showMetadata ? 'translate-x-0 opacity-100' : 'translate-x-[110%] opacity-0 pointer-events-none'}`
          }`}
          style={isMobile ? { paddingTop: 'env(safe-area-inset-top, 0px)' } : { top: '5.5rem' }}
          onClick={(e) => e.stopPropagation()}
        >
          <div className={`h-full bg-gradient-to-br from-gray-900/95 to-black/95 backdrop-blur-xl overflow-hidden ${
            isMobile ? 'rounded-t-2xl' : 'rounded-2xl border border-white/30 shadow-[0_20px_60px_rgba(0,0,0,0.8)]'
          }`}>
            <div className="p-6 h-full overflow-y-auto">
              <div className="flex items-center justify-between mb-6 pb-4 border-b border-white/20">
                <h3 className="text-white font-semibold text-lg">Details</h3>
                {isMobile && (
                  <button
                    onClick={() => setShowMetadata(false)}
                    className="p-2 hover:bg-white/10 rounded-lg text-white/70"
                  >
                    <X className="w-5 h-5" />
                  </button>
                )}
              </div>

              <div className="space-y-3 text-sm">
                {/* Description */}
                {currentItem.description && (
                  <div>
                    <div className="text-white/60 mb-1">Description</div>
                    <div className="text-white text-xs max-h-32 overflow-y-auto whitespace-pre-wrap">
                      {currentItem.description}
                    </div>
                  </div>
                )}

                {currentItem.description && <div className="border-t border-white/10 pt-3 mt-3" />}

                {/* Person */}
                {currentItem.person && (
                  <div>
                    <div className="text-white/60 mb-1 flex items-center gap-2">
                      <User className="w-4 h-4" />
                      Person
                    </div>
                    <div className="text-white">{currentItem.person.name}</div>
                  </div>
                )}

                {/* Filename */}
                <div>
                  <div className="text-white/60 mb-1 flex items-center gap-2">
                    <ImageIcon className="w-4 h-4" />
                    Filename
                  </div>
                  <div className="text-white break-all">
                    {currentItem.filename || `File ${currentItem.id}`}
                  </div>
                </div>

                {/* Resolution */}
                {(currentItem.width && currentItem.height) || naturalSize ? (
                  <div>
                    <div className="text-white/60 mb-1 flex items-center gap-2">
                      <ImageIcon className="w-4 h-4" />
                      Resolution
                    </div>
                    <div className="text-white">
                      {currentItem.width && currentItem.height
                        ? `${currentItem.width} x ${currentItem.height}`
                        : naturalSize
                          ? `${naturalSize.width} x ${naturalSize.height}`
                          : 'Loading...'}
                    </div>
                  </div>
                ) : null}

                {/* File Size */}
                {currentItem.file_size && currentItem.file_size > 0 && (
                  <div>
                    <div className="text-white/60 mb-1 flex items-center gap-2">
                      <HardDrive className="w-4 h-4" />
                      Size
                    </div>
                    <div className="text-white">{formatBytes(currentItem.file_size)}</div>
                  </div>
                )}

                {/* Duration (for videos) */}
                {isCurrentVideo && currentItem.duration && currentItem.duration > 0 && (
                  <div>
                    <div className="text-white/60 mb-1 flex items-center gap-2">
                      <Clock className="w-4 h-4" />
                      Duration
                    </div>
                    <div className="text-white">{formatDuration(currentItem.duration)}</div>
                  </div>
                )}

                {/* Post Date */}
                {currentItem.post_date && (
                  <div>
                    <div className="text-white/60 mb-1 flex items-center gap-2">
                      <Calendar className="w-4 h-4" />
                      Post Date
                    </div>
                    <div className="text-white">{formatDate(currentItem.post_date)}</div>
                  </div>
                )}

                {/* Media Date */}
                {currentItem.media_date && (
                  <div>
                    <div className="text-white/60 mb-1 flex items-center gap-2">
                      <Clock className="w-4 h-4" />
                      Media Date
                    </div>
                    <div className="text-white">{formatDate(currentItem.media_date)}</div>
                  </div>
                )}

                {/* Relationship */}
                {currentItem.person?.relationship && (
                  <div>
                    <div className="text-white/60 mb-1 flex items-center gap-2">
                      <User className="w-4 h-4" />
                      Relationship
                    </div>
                    <span
                      className="px-2 py-0.5 rounded text-xs text-white inline-block"
                      style={{ backgroundColor: currentItem.person.relationship.color }}
                    >
                      {currentItem.person.relationship.name}
                    </span>
                  </div>
                )}

                {/* Tags */}
                {currentItem.tags && currentItem.tags.length > 0 && (
                  <div>
                    <div className="text-white/60 mb-1">Tags</div>
                    <div className="flex flex-wrap gap-1">
                      {currentItem.tags.map(tag => (
                        <span
                          key={tag.id}
                          className="px-2 py-0.5 rounded text-xs text-white"
                          style={{ backgroundColor: tag.color }}
                        >
                          {tag.name}
                        </span>
                      ))}
                    </div>
                  </div>
                )}

              </div>
            </div>
          </div>
        </div>

        {/* ===== MOBILE BUNDLE SIDEBAR ===== */}
        {isMobile && items.length > 1 && (
          <div
            className="fixed left-0 z-20 bg-black/80 backdrop-blur-sm rounded-r-lg transition-all duration-200"
            style={{ top: 'env(safe-area-inset-top, 0px)' }}
          >
            {/* Bundle Header */}
            <button
              onClick={(e) => { e.stopPropagation(); setSidebarOpen(!sidebarOpen) }}
              className="px-2 py-2 flex items-center gap-1 hover:bg-white/10 w-full"
            >
              <span className="text-white text-xs font-medium">{sidebarOpen ? 'Gallery' : ''}</span>
              <ChevronLeft className={`w-4 h-4 text-white transition-transform ${sidebarOpen ? '' : 'rotate-180'}`} />
            </button>

            {/* Thumbnail Strip - vertical, collapsible */}
            {sidebarOpen && (
              <div className="px-2 pb-2 space-y-2 max-h-[60vh] overflow-y-auto">
                {items.map((item, idx) => {
                  const isVideo = item.file_type === 'video'
                  const isCurrent = idx === currentIndex
                  return (
                    <button
                      key={item.id}
                      onClick={(e) => { e.stopPropagation(); onNavigate(idx) }}
                      className={`relative w-16 h-16 rounded-lg overflow-hidden transition-all ${
                        isCurrent
                          ? 'ring-2 ring-blue-500'
                          : 'opacity-50'
                      }`}
                    >
                      <img
                        src={getThumbnailUrl(item)}
                        alt=""
                        className="w-full h-full object-cover"
                      />
                      {isVideo && (
                        <div className="absolute inset-0 flex items-center justify-center bg-black/40">
                          <Video className="w-4 h-4 text-white" />
                        </div>
                      )}
                    </button>
                  )
                })}
              </div>
            )}
          </div>
        )}

        {/* ===== BOTTOM BAR ===== */}
        {/* Videos: positioned based on video size. Images: bottom left corner of screen */}
        <div
          className={`fixed flex items-center transition-opacity duration-200 z-20 ${
            isCurrentVideo && !isLandscape && videoAspectRatio && videoAspectRatio >= 1
              ? 'left-4 right-4'
              : `left-3 right-3 ${!isMobile ? 'bottom-4 right-auto max-w-md justify-start' : 'bottom-1 justify-between'}`
          }`}
          style={{
            ...(isCurrentVideo && !isLandscape && videoAspectRatio && videoAspectRatio >= 1 ? { bottom: isMobile ? '4px' : '104px' } : {}),
            paddingBottom: 'env(safe-area-inset-bottom, 0px)',
          }}
        >
          <div className="flex items-start gap-3 min-w-0 flex-1">
            {/* Person Avatar */}
            <div className="w-9 h-9 rounded-full bg-gradient-to-br from-indigo-500 to-purple-500 p-0.5 flex-shrink-0 mt-0.5">
              <div className="w-full h-full rounded-full overflow-hidden bg-black flex items-center justify-center">
                <User className="w-4 h-4 text-white/50" />
              </div>
            </div>

            {/* Person name and description */}
            <div className="min-w-0 flex-1">
              <div className="flex items-center gap-2">
                <span className="text-white text-sm font-medium drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)]">
                  {currentItem?.person?.name || 'Unknown'}
                </span>
                <span className="text-white/60 text-xs drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)]">
                  {formatDate(currentItem?.media_date)}
                </span>
              </div>
              {currentItem?.description && (
                <p className="text-white/70 text-sm drop-shadow-[0_1px_2px_rgba(0,0,0,0.9)] leading-relaxed">
                  {currentItem.description.slice(0, isCurrentVideo ? 50 : 80)}
                  {currentItem.description.length > (isCurrentVideo ? 50 : 80) && '...'}
                </p>
              )}
              {currentItem?.tags && currentItem.tags.length > 0 && (
                <div className="flex flex-wrap gap-1 mt-1">
                  {currentItem.tags.slice(0, 3).map(tag => (
                    <span
                      key={tag.id}
                      className="px-1.5 py-0.5 rounded text-xs text-white"
                      style={{ backgroundColor: tag.color }}
                    >
                      {tag.name}
                    </span>
                  ))}
                </div>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
