import { useState, useCallback, useEffect, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { api, endpoints, authenticatedApiUrl } from '../../api'
import MediaList from './MediaList'
import VideoPlayer, { type VideoPlayerHandle } from './VideoPlayer'
import CanvasEditor, { type CanvasEditorHandle } from './CanvasEditor'
import AnnotationsSidebar from './AnnotationsSidebar'
import Scrubber, { type ScrubberMark } from './Scrubber'
import { DetectionClasses, useFlight } from '../../components'
import { useSavedAnnotations } from '../../components/SavedAnnotationsContext'
import { AnnotationSource, AnnotationStatus, MediaType } from '../../types'
import { getClassColor, getClassNameFallback, getPhotoModeSuffix } from '../../class-colors'
import { captureThumbnails } from './thumbnail'
import { formatTime, formatTicks, parseAnnotationTime } from './time'
import type { Media, AnnotationListItem, Detection } from '../../types'

const FRAME_STEPS = [1, 5, 10, 30, 60]

const FAKE_LOG_LINES = [
  '[tile 04/16] 2 candidates',
  '[tile 05/16] 1 candidate (conf 0.94)',
  '[filter] min_conf=0.25…',
]

export default function AnnotationsPage() {
  const { t } = useTranslation()
  const [selectedMedia, setSelectedMedia] = useState<Media | null>(null)
  const [currentTime, setCurrentTime] = useState(0)
  const [duration, setDuration] = useState(0)
  const [annotations, setAnnotations] = useState<AnnotationListItem[]>([])
  const [selectedAnnotation, setSelectedAnnotation] = useState<AnnotationListItem | null>(null)
  const [selectedClassNum, setSelectedClassNum] = useState(0)
  const [photoMode, setPhotoMode] = useState(0)
  const [detections, setDetections] = useState<Detection[]>([])
  const [zoom, setZoom] = useState(1)
  const [cursor, setCursor] = useState<{ x: number; y: number } | null>(null)
  const [isPlaying, setIsPlaying] = useState(false)
  const [volume, setVolume] = useState(0.62)
  const [muted, setMuted] = useState(false)
  const [aiDetecting, setAiDetecting] = useState(false)
  const [aiLog, setAiLog] = useState<string[]>([])
  const [aiProgress, setAiProgress] = useState(0)
  const aiStartRef = useRef<number>(0)
  const aiCloseTimerRef = useRef<number | null>(null)
  const [aiElapsed, setAiElapsed] = useState(0)
  const videoPlayerRef = useRef<VideoPlayerHandle>(null)
  const canvasRef = useRef<CanvasEditorHandle>(null)
  const { addMany } = useSavedAnnotations()
  const { selectedFlight } = useFlight()

  const isVideo = selectedMedia?.mediaType === MediaType.Video

  useEffect(() => {
    setDetections([])
    setSelectedAnnotation(null)
    setCurrentTime(0)
    setDuration(0)
    setIsPlaying(false)
    setMuted(false)
  }, [selectedMedia])

  // Push the page's initial volume into the <video> element once the player
  // is mounted — otherwise the slider shows 62% while audio plays at 100%.
  useEffect(() => {
    if (!selectedMedia || !isVideo) return
    videoPlayerRef.current?.setVolume(volume)
    // Only on media change — subsequent slider drags push via onVolumeChange.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selectedMedia, isVideo])

  // AI detection fake-log progress
  useEffect(() => {
    if (!aiDetecting) return
    aiStartRef.current = performance.now()
    setAiElapsed(0)
    setAiLog([])
    setAiProgress(0)
    let i = 0
    const logTimer = window.setInterval(() => {
      if (i < FAKE_LOG_LINES.length) {
        setAiLog(prev => [...prev, FAKE_LOG_LINES[i]])
        i++
      }
    }, 700)
    const tickTimer = window.setInterval(() => {
      setAiElapsed((performance.now() - aiStartRef.current) / 1000)
      setAiProgress(p => Math.min(0.95, p + 0.04))
    }, 100)
    return () => {
      window.clearInterval(logTimer)
      window.clearInterval(tickTimer)
    }
  }, [aiDetecting])

  const scrubberMarks = useMemo<ScrubberMark[]>(() => {
    return annotations
      .map(a => {
        const sec = parseAnnotationTime(a.time)
        if (sec == null) return null
        const first = a.detections[0]
        return { time: sec, color: first ? getClassColor(first.classNum) : '#9AA4B2' }
      })
      .filter((m): m is ScrubberMark => m !== null)
  }, [annotations])

  const handleSave = useCallback(async () => {
    if (!selectedMedia || !detections.length) return
    const time = selectedMedia.mediaType === MediaType.Video ? formatTicks(currentTime) : null
    const body = { mediaId: selectedMedia.id, time, detections }

    const { fullFrame, detectionThumbnails } = await captureThumbnails(
      selectedMedia,
      videoPlayerRef.current?.getVideoElement() ?? null,
      detections,
    )

    const pushToStore = (annotationLocalId: string) => {
      const createdDate = new Date().toISOString()
      addMany(detections.map((d, i) => ({
        id: `${annotationLocalId}:${d.id ?? i}`,
        annotationLocalId,
        mediaId: selectedMedia.id,
        mediaName: selectedMedia.name,
        thumbnail: detectionThumbnails[i] ?? '',
        fullFrame,
        status: AnnotationStatus.Created,
        source: AnnotationSource.Manual,
        createdDate,
        detection: d,
        time,
        flightId: selectedFlight?.id ?? null,
      })))
    }

    if (!selectedMedia.path.startsWith('blob:')) {
      try {
        await api.post(endpoints.annotations.annotations(), body)
        const res = await api.get<{ items: AnnotationListItem[] }>(
          endpoints.annotations.annotationsByMedia(selectedMedia.id),
        )
        setAnnotations(res.items)
        pushToStore(`saved-${crypto.randomUUID()}`)
        return
      } catch {
        // fall through to local save
      }
    }

    const local: AnnotationListItem = {
      id: `local-${crypto.randomUUID()}`,
      mediaId: selectedMedia.id,
      time,
      createdDate: new Date().toISOString(),
      userId: 'local',
      source: AnnotationSource.Manual,
      status: AnnotationStatus.Created,
      isSplit: false,
      splitTile: null,
      detections: [...detections],
    }
    setAnnotations(prev => [...prev, local])
    pushToStore(local.id)
  }, [selectedMedia, detections, currentTime, addMany, selectedFlight])

  const handleDownload = useCallback(async (ann: AnnotationListItem) => {
    if (!selectedMedia) return

    const txt = ann.detections
      .map(d => `${d.classNum} ${d.centerX.toFixed(6)} ${d.centerY.toFixed(6)} ${d.width.toFixed(6)} ${d.height.toFixed(6)}`)
      .join('\n')
    const stem = `annotation_${new Date().toISOString().replace(/[:.]/g, '-')}`

    const txtBlob = new Blob([txt], { type: 'text/plain' })
    const txtUrl = URL.createObjectURL(txtBlob)
    const txtA = document.createElement('a')
    txtA.href = txtUrl
    txtA.download = `${stem}.txt`
    txtA.click()
    URL.revokeObjectURL(txtUrl)

    const videoEl = videoPlayerRef.current?.getVideoElement() ?? null
    let w = 0, h = 0
    const canvas = document.createElement('canvas')
    if (videoEl && videoEl.videoWidth) {
      w = videoEl.videoWidth
      h = videoEl.videoHeight
      canvas.width = w
      canvas.height = h
      const ctx = canvas.getContext('2d')
      if (ctx) ctx.drawImage(videoEl, 0, 0, w, h)
    } else if (!selectedMedia.path) {
      return
    } else {
      const img = new Image()
      img.crossOrigin = 'anonymous'
      img.src = selectedMedia.path.startsWith('blob:')
        ? selectedMedia.path
        : authenticatedApiUrl(endpoints.annotations.mediaFile(selectedMedia.id))
      await new Promise(res => { img.onload = res; img.onerror = res })
      w = img.naturalWidth
      h = img.naturalHeight
      canvas.width = w
      canvas.height = h
      const ctx = canvas.getContext('2d')
      if (ctx) ctx.drawImage(img, 0, 0, w, h)
    }

    if (w && h) {
      const ctx = canvas.getContext('2d')
      if (ctx) {
        ctx.lineWidth = 2
        ctx.font = '14px sans-serif'
        const labelH = 17
        const padX = 4
        for (const d of ann.detections) {
          const bx = (d.centerX - d.width / 2) * w
          const by = (d.centerY - d.height / 2) * h
          const bw = d.width * w
          const bh = d.height * h
          const color = getClassColor(d.classNum)

          ctx.strokeStyle = color
          ctx.strokeRect(bx, by, bw, bh)

          const name = d.label || getClassNameFallback(d.classNum)
          const modeSuffix = getPhotoModeSuffix(d.classNum)
          const confSuffix = d.confidence < 0.995 ? ` ${(d.confidence * 100).toFixed(0)}%` : ''
          const label = `${name}${modeSuffix}${confSuffix}`

          const metrics = ctx.measureText(label)
          const labelW = metrics.width + padX * 2
          ctx.fillStyle = color
          ctx.fillRect(bx, Math.max(0, by - labelH), labelW, labelH)
          ctx.fillStyle = '#000'
          ctx.fillText(label, bx + padX, Math.max(13, by - 4))
        }
      }
      canvas.toBlob(blob => {
        if (!blob) return
        const url = URL.createObjectURL(blob)
        const a = document.createElement('a')
        a.href = url
        a.download = `${stem}.png`
        a.click()
        URL.revokeObjectURL(url)
      }, 'image/png')
    }
  }, [selectedMedia])

  const handleAnnotationSelect = useCallback((ann: AnnotationListItem) => {
    setSelectedAnnotation(ann)
    setDetections(ann.detections)
    const sec = parseAnnotationTime(ann.time)
    if (sec != null) {
      videoPlayerRef.current?.seek(sec)
      setCurrentTime(sec)
    }
  }, [])

  const handleDetectionsChange = useCallback((dets: Detection[]) => {
    setDetections(dets)
  }, [])

  const handleAiDetect = useCallback(async () => {
    if (!selectedMedia || aiDetecting) return
    if (aiCloseTimerRef.current != null) {
      window.clearTimeout(aiCloseTimerRef.current)
      aiCloseTimerRef.current = null
    }
    setAiDetecting(true)
    try {
      await api.post(endpoints.detect.media(selectedMedia.id))
    } catch {
      // banner stays visible briefly; sidebar SSE refresh will pick up results
    } finally {
      setAiProgress(1)
      aiCloseTimerRef.current = window.setTimeout(() => {
        aiCloseTimerRef.current = null
        setAiDetecting(false)
      }, 500)
    }
  }, [selectedMedia, aiDetecting])

  // Clear any pending AI-banner close timer on unmount.
  useEffect(() => () => {
    if (aiCloseTimerRef.current != null) {
      window.clearTimeout(aiCloseTimerRef.current)
      aiCloseTimerRef.current = null
    }
  }, [])

  const togglePlay = () => { videoPlayerRef.current?.toggle() }
  const stepFrames = (n: number) => { videoPlayerRef.current?.frameStep(n) }
  const seekRel = (sec: number) => {
    const p = videoPlayerRef.current
    if (!p) return
    p.seek(Math.max(0, Math.min(p.getDuration(), p.getCurrentTime() + sec)))
  }

  const onVolumeChange = (v: number) => {
    setVolume(v)
    videoPlayerRef.current?.setVolume(v)
  }
  const toggleMute = () => {
    // VideoPlayer.toggleMute() fires onMutedChange, which updates `muted` —
    // don't flip parent state independently or the two desync (e.g. M-key
    // shortcut already routed via onMutedChange).
    videoPlayerRef.current?.toggleMute()
  }

  const dims = (() => {
    const v = videoPlayerRef.current?.getVideoElement()
    if (!v || !v.videoWidth) return null
    return { w: v.videoWidth, h: v.videoHeight }
  })()
  const fps = videoPlayerRef.current?.getFrameRate() ?? 30
  const currentFrame = isVideo ? Math.floor(currentTime * fps) : 0
  const totalFrames = isVideo ? Math.floor(duration * fps) : 0

  const detectionsLabel = `${detections.length} det${detections.length !== 1 ? 's' : ''}`

  return (
    <div className="flex h-full">
      {/* LEFT SIDEBAR */}
      <div style={{ width: 232 }} className="bg-surface-1 flex flex-col shrink-0 border-r border-border-hair">
        <MediaList
          selectedMedia={selectedMedia}
          onSelect={setSelectedMedia}
          onAnnotationsLoaded={setAnnotations}
        />
        <DetectionClasses
          selectedClassNum={selectedClassNum}
          onSelect={setSelectedClassNum}
          photoMode={photoMode}
          onPhotoModeChange={setPhotoMode}
        />
      </div>
      {/* CENTER */}
      <div className="flex-1 flex flex-col min-w-0 bg-surface-0">
        {/* Canvas top bar */}
        <div className="h-9 flex items-center gap-3 px-4 border-b border-border-hair bg-surface-1 shrink-0">
          <div className="flex items-center gap-2">
            <span className="sect-head">{t('annotations.canvas')}</span>
            {selectedMedia && (
              <>
                <span className="mono text-[11px] text-text-muted">{selectedMedia.name}</span>
                {dims && (
                  <span className="mono text-[10px] px-1.5 py-0.5 border border-border-hair text-text-secondary">
                    {dims.w}×{dims.h} · {fps} FPS
                  </span>
                )}
              </>
            )}
          </div>
          <div className="ml-auto flex items-center gap-2">
            <span className="micro">{t('annotations.zoom')}</span>
            <span className="mono text-[11px] text-text-primary">{Math.round(zoom * 100)}%</span>
            <span className="mx-2 h-4 w-px bg-border-hair" />
            <span className="micro">{t('annotations.cursor')}</span>
            <span className="mono text-[11px] text-text-primary">
              {cursor ? `${cursor.x.toFixed(3)}, ${cursor.y.toFixed(3)}` : '—'}
            </span>
            <span className="mx-2 h-4 w-px bg-border-hair" />
            <span className="mono text-[11px] text-text-secondary">{detectionsLabel}</span>
          </div>
        </div>

        {/* Canvas area */}
        <div className="flex-1 relative overflow-hidden">
          {selectedMedia && isVideo && (
            <VideoPlayer
              ref={videoPlayerRef}
              media={selectedMedia}
              onTimeUpdate={setCurrentTime}
              onPlayingChange={setIsPlaying}
              onDurationChange={setDuration}
              onMutedChange={setMuted}
            >
              <CanvasEditor
                ref={canvasRef}
                media={selectedMedia}
                annotation={selectedAnnotation}
                detections={detections}
                onDetectionsChange={handleDetectionsChange}
                selectedClassNum={selectedClassNum}
                currentTime={currentTime}
                annotations={annotations}
                onZoomChange={setZoom}
                onCursorChange={(x, y) => setCursor({ x, y })}
              />
            </VideoPlayer>
          )}
          {selectedMedia && !isVideo && (
            <CanvasEditor
              ref={canvasRef}
              media={selectedMedia}
              annotation={selectedAnnotation}
              detections={detections}
              onDetectionsChange={handleDetectionsChange}
              selectedClassNum={selectedClassNum}
              currentTime={currentTime}
              annotations={annotations}
              onZoomChange={setZoom}
              onCursorChange={(x, y) => setCursor({ x, y })}
            />
          )}
          {!selectedMedia && (
            <div className="absolute inset-0 flex items-center justify-center text-text-muted text-sm">
              {t('annotations.selectMedia')}
            </div>
          )}

          {/* AI Detection floating banner */}
          {aiDetecting && (
            <div className="absolute top-6 right-6 ai-banner px-3 py-2 w-72">
              <div className="flex items-center gap-2 mb-1.5">
                <span className="live-dot" />
                <span className="micro text-accent-cyan">{t('annotations.detectInProgress')}</span>
                <span className="ml-auto mono text-[10px] text-text-muted">{aiElapsed.toFixed(1)}s</span>
              </div>
              <div className="mono text-[10px] space-y-0.5 text-text-secondary">
                {aiLog.map((line, i) => <div key={i}>{line}</div>)}
              </div>
              <div className="mt-2 h-[2px] bg-black/40 overflow-hidden">
                <div style={{ height: '100%', width: `${aiProgress * 100}%`, background: 'var(--accent-cyan)' }} />
              </div>
            </div>
          )}
        </div>

        {/* Scrubber + Controls */}
        {selectedMedia && isVideo && (
          <div className="border-t border-border-hair bg-surface-1 shrink-0">
            <div className="px-4 pt-3 pb-2">
              <Scrubber
                current={currentTime}
                duration={duration}
                marks={scrubberMarks}
                onSeek={t => { videoPlayerRef.current?.seek(t); setCurrentTime(t) }}
              />
            </div>
            <div className="px-4 pb-3 flex items-center gap-1.5 min-w-0 whitespace-nowrap overflow-hidden">
              <div className="flex items-center gap-1 p-1 border border-border-hair rounded-[2px]">
                <button className="ibtn" style={{ width: 28, height: 28, border: 0, background: 'transparent' }} title={t('annotations.previousMedia')}>
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg>
                </button>
                <button className="ibtn" style={{ width: 28, height: 28, border: 0, background: 'transparent' }} title={t('annotations.back5s')} onClick={() => seekRel(-5)}>
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M11 18V6l-8.5 6zM22 18V6l-8.5 6z"/></svg>
                </button>
                <button
                  className="ibtn"
                  title={isPlaying ? t('annotations.pause') : t('annotations.play')}
                  onClick={togglePlay}
                  style={{
                    width: 28,
                    height: 28,
                    background: isPlaying ? 'rgba(255,157,61,0.12)' : 'transparent',
                    color: isPlaying ? 'var(--accent-amber)' : undefined,
                    borderColor: isPlaying ? 'var(--accent-amber)' : 'transparent',
                  }}
                >
                  {isPlaying
                    ? <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M6 5h4v14H6zM14 5h4v14h-4z"/></svg>
                    : <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>}
                </button>
                <button className="ibtn" style={{ width: 28, height: 28, border: 0, background: 'transparent' }} title={t('annotations.forward5s')} onClick={() => seekRel(5)}>
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M13 6v12l8.5-6zM2 6v12l8.5-6z"/></svg>
                </button>
                <button className="ibtn" style={{ width: 28, height: 28, border: 0, background: 'transparent' }} title={t('annotations.nextMedia')}>
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M16 6h2v12h-2zM6 18l8.5-6L6 6z"/></svg>
                </button>
              </div>

              <span className="micro">{t('annotations.frameStep')}</span>
              <div className="flex items-center gap-1 p-1 border border-border-hair rounded-[2px]">
                {FRAME_STEPS.map(n => (
                  <button
                    key={n}
                    onClick={() => stepFrames(n)}
                    className="ibtn mono"
                    style={{ width: 30, height: 28, fontSize: 10, border: 0, background: 'transparent', letterSpacing: 0 }}
                  >
                    {n}
                  </button>
                ))}
              </div>

              <span className="mx-1 h-5 w-px bg-border-hair" />

              <button
                onClick={handleSave}
                disabled={!detections.length}
                className="btn btn-secondary"
              >
                <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8M7 3v5h8"/></svg>
                {t('annotations.save')}
              </button>
              <button
                onClick={() => canvasRef.current?.deleteSelected()}
                disabled={!detections.length}
                className="btn btn-danger-ghost"
              >
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg>
                {t('annotations.delete')}
              </button>
              <button
                onClick={() => canvasRef.current?.deleteAll()}
                disabled={!detections.length}
                className="btn btn-danger-ghost"
                title={t('annotations.deleteAllTitle')}
              >
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11l4 6M14 11l-4 6"/></svg>
                {t('annotations.deleteAll')}
              </button>

              <span className="mx-1 h-5 w-px bg-border-hair" />

              <button
                onClick={handleAiDetect}
                disabled={!selectedMedia || aiDetecting}
                className="btn btn-primary"
              >
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 7V3h4"/><path d="M17 3h4v4"/><path d="M21 17v4h-4"/><path d="M7 21H3v-4"/><circle cx="12" cy="12" r="1.6" fill="currentColor" stroke="none"/></svg>
                {t('annotations.detect')}
                <span className="ml-1 mono opacity-70" style={{ fontSize: 9 }}>[R]</span>
              </button>

              <span className="mx-1 h-5 w-px bg-border-hair" />

              <div className="ml-auto flex items-center gap-2">
                <button className="ibtn" style={{ width: 28, height: 28 }} title={t('annotations.mute')} onClick={toggleMute}>
                  {muted
                    ? <svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.21.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.95 8.95 0 0 0 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.17v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></svg>
                    : <svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4z"/></svg>}
                </button>
                <input
                  type="range"
                  className="vol"
                  min={0}
                  max={100}
                  value={Math.round(volume * 100)}
                  onChange={e => onVolumeChange(Number(e.target.value) / 100)}
                />
                <span className="mono text-[10px] text-text-muted" style={{ width: 24 }}>{Math.round(volume * 100)}</span>
              </div>
            </div>

            {/* Status bar */}
            <div className="px-4 h-7 flex items-center border-t border-border-hair bg-surface-0">
              <span className="mono text-[11px] text-text-primary">{formatTime(currentTime, true)}</span>
              <span className="mono text-[11px] mx-1.5 text-text-muted">/</span>
              <span className="mono text-[11px] text-text-secondary">{formatTime(duration, true)}</span>
              <span className="mx-3 h-4 w-px bg-border-hair" />
              <span className="micro">{t('annotations.frame')}</span>
              <span className="mono text-[11px] ml-1.5 text-text-primary">{currentFrame} / {totalFrames}</span>
            </div>
          </div>
        )}

        {/* Photo-only controls row (save/delete/AI detect) */}
        {selectedMedia && !isVideo && (
          <div className="border-t border-border-hair bg-surface-1 shrink-0 px-4 py-2 flex items-center gap-3">
            <button onClick={handleSave} disabled={!detections.length} className="btn btn-secondary">{t('annotations.save')}</button>
            <button onClick={() => canvasRef.current?.deleteSelected()} disabled={!detections.length} className="btn btn-danger-ghost">{t('annotations.delete')}</button>
            <button onClick={() => canvasRef.current?.deleteAll()} disabled={!detections.length} className="btn btn-danger-ghost">{t('annotations.deleteAll')}</button>
            <span className="mx-1 h-5 w-px bg-border-hair" />
            <button onClick={handleAiDetect} disabled={!selectedMedia || aiDetecting} className="btn btn-primary">{t('annotations.detect')}</button>
            <span className="ml-auto mono text-[11px] text-text-muted">{detectionsLabel}</span>
          </div>
        )}
      </div>

      {/* RIGHT SIDEBAR */}
      <div style={{ width: 208 }} className="bg-surface-1 flex flex-col shrink-0 border-l border-border-hair">
        <AnnotationsSidebar
          media={selectedMedia}
          annotations={annotations}
          selectedAnnotation={selectedAnnotation}
          onSelect={handleAnnotationSelect}
          onAnnotationsUpdate={setAnnotations}
          onDownload={handleDownload}
        />
      </div>
    </div>
  )
}
