import { useState, useEffect, useCallback, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { useDropzone } from 'react-dropzone'
import { useFlight, ConfirmDialog } from '../../components'
import { api, endpoints } from '../../api'
import { useDebounce } from '../../hooks'
import { MediaType } from '../../types'
import type { Media, PaginatedResponse, AnnotationListItem } from '../../types'

interface Props {
  selectedMedia: Media | null
  onSelect: (m: Media) => void
  onAnnotationsLoaded: (anns: AnnotationListItem[]) => void
}

export default function MediaList({ selectedMedia, onSelect, onAnnotationsLoaded }: Props) {
  const { t } = useTranslation()
  const { selectedFlight } = useFlight()
  const [media, setMedia] = useState<Media[]>([])
  const [filter, setFilter] = useState('')
  const debouncedFilter = useDebounce(filter, 300)
  const [deleteId, setDeleteId] = useState<string | null>(null)
  const folderInputRef = useRef<HTMLInputElement>(null)
  const fileInputRef = useRef<HTMLInputElement>(null)

  const fetchMedia = useCallback(async () => {
    const params = new URLSearchParams({ pageSize: '1000' })
    if (selectedFlight) params.set('flightId', selectedFlight.id)
    if (debouncedFilter) params.set('name', debouncedFilter)
    try {
      const res = await api.get<PaginatedResponse<Media>>(endpoints.annotations.media(params.toString()))
      setMedia(prev => {
        // Keep local-only (blob URL) entries, merge with backend entries
        const local = prev.filter(m => m.path.startsWith('blob:'))
        return [...local, ...res.items]
      })
    } catch {
      // backend unavailable — keep local-only entries
    }
  }, [selectedFlight, debouncedFilter])

  useEffect(() => { fetchMedia() }, [fetchMedia])

  useEffect(() => {
    return () => {
      for (const m of media) if (m.path.startsWith('blob:')) URL.revokeObjectURL(m.path)
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const handleSelect = async (m: Media) => {
    onSelect(m)
    if (m.path.startsWith('blob:')) {
      onAnnotationsLoaded([])
      return
    }
    try {
      const res = await api.get<PaginatedResponse<AnnotationListItem>>(
        endpoints.annotations.annotationsByMedia(m.id),
      )
      onAnnotationsLoaded(res.items)
    } catch {
      onAnnotationsLoaded([])
    }
  }

  const handleDelete = async () => {
    if (!deleteId) return
    const target = media.find(m => m.id === deleteId)
    if (target?.path.startsWith('blob:')) {
      URL.revokeObjectURL(target.path)
      setMedia(prev => prev.filter(m => m.id !== deleteId))
      setDeleteId(null)
      return
    }
    try { await api.delete(endpoints.annotations.mediaItem(deleteId)) } catch {}
    setDeleteId(null)
    fetchMedia()
  }

  const uploadFiles = useCallback(async (files: File[] | FileList) => {
    if (!files.length) return
    const arr = Array.from(files)

    // Try backend first
    if (selectedFlight) {
      try {
        const form = new FormData()
        form.append('waypointId', '')
        for (const file of arr) form.append('files', file)
        await api.upload(endpoints.annotations.mediaBatch(), form)
        fetchMedia()
        return
      } catch {
        // fall through to local mode
      }
    }

    // Local mode: add blob URL entries to state
    const videoExts = /\.(mp4|mov|webm|mkv|avi|m4v|ogg|ogv)$/i
    const imageExts = /\.(jpe?g|png|webp|gif|bmp|tiff?)$/i
    const accepted: File[] = []
    const rejected: string[] = []
    for (const file of arr) {
      const isVideo = file.type.startsWith('video/') || videoExts.test(file.name)
      const isImage = file.type.startsWith('image/') || imageExts.test(file.name)
      if (isVideo || isImage) accepted.push(file)
      else rejected.push(file.name)
    }
    if (rejected.length) {
      alert(`Unsupported file type (video/image only):\n${rejected.join('\n')}`)
    }

    const localItems: Media[] = accepted.map(file => {
      const isVideo = file.type.startsWith('video/') || videoExts.test(file.name)
      return {
        id: `local-${crypto.randomUUID()}`,
        name: file.name,
        path: URL.createObjectURL(file),
        mediaType: isVideo ? MediaType.Video : MediaType.Image,
        mediaStatus: 0,
        duration: null,
        annotationCount: 0,
        waypointId: null,
        userId: 'local',
      }
    })
    setMedia(prev => [...localItems, ...prev])
  }, [selectedFlight, fetchMedia])

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop: uploadFiles,
    multiple: true,
    noClick: true,
    noKeyboard: true,
  })

  const handleFolderInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files?.length) uploadFiles(e.target.files)
    e.target.value = ''
  }

  const filtered = media.filter(m => m.name.toLowerCase().includes(filter.toLowerCase()))

  return (
    <div
      {...getRootProps({
        className: `flex flex-col flex-1 min-h-0 bg-surface-1${isDragActive ? ' ring-2 ring-accent-amber ring-inset' : ''}`,
      })}
    >
      {/* Dropzone hidden input */}
      <input {...getInputProps()} />

      {/* Hidden file inputs */}
      <input
        ref={fileInputRef}
        type="file"
        multiple
        className="hidden"
        onChange={e => {
          if (e.target.files?.length) uploadFiles(e.target.files)
          e.target.value = ''
        }}
      />
      <input
        ref={folderInputRef}
        type="file"
        multiple
        className="hidden"
        // @ts-expect-error webkitdirectory is non-standard but widely supported
        webkitdirectory=""
        directory=""
        onChange={handleFolderInput}
      />

      {/* Header row */}
      <div className="flex items-center justify-between px-3 h-9 border-b border-border-hair shrink-0">
        <div className="flex items-center gap-2">
          <span className="sect-head">{t('annotations.mediaList')}</span>
          <span className="mono text-[10px] text-text-muted">{filtered.length}</span>
        </div>
        <div className="flex items-center gap-1">
          {/* Upload file button */}
          <button
            type="button"
            className="ibtn"
            style={{ width: 22, height: 22 }}
            title={t('annotations.upload')}
            onClick={() => fileInputRef.current?.click()}
          >
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M12 5v14M5 12h14"/>
            </svg>
          </button>
          {/* Open folder button */}
          <button
            type="button"
            className="ibtn"
            style={{ width: 22, height: 22 }}
            title="Open Folder"
            onClick={() => folderInputRef.current?.click()}
          >
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
            </svg>
          </button>
        </div>
      </div>

      {/* Filter input row */}
      <div className="px-3 py-2 border-b border-border-hair shrink-0">
        <div className="relative">
          <svg
            width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"
            className="absolute left-2 top-1/2 -translate-y-1/2 text-text-muted pointer-events-none"
          >
            <circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/>
          </svg>
          <input
            className="inp w-full pl-7"
            style={{ height: 28, padding: '0 10px 0 28px' }}
            value={filter}
            onChange={e => setFilter(e.target.value)}
            placeholder={t('annotations.filterByName')}
          />
        </div>
      </div>

      {/* List */}
      <div className="flex-1 overflow-y-auto min-h-0">
        {filtered.map(m => {
          const isActive = selectedMedia?.id === m.id
          const isVideo = m.mediaType === MediaType.Video
          const hasDuration = !!m.duration
          const durationColor = isActive
            ? 'text-accent-amber'
            : hasDuration
              ? 'text-text-secondary'
              : 'text-text-muted'

          return (
            <div
              key={m.id}
              onClick={() => handleSelect(m)}
              onContextMenu={e => { e.preventDefault(); setDeleteId(m.id) }}
              className={`media-row${isActive ? ' active' : ''}`}
            >
              {isVideo
                ? <span className="chip-video">VIDEO</span>
                : <span className="chip-photo">PHOTO</span>
              }
              <span className={`truncate${isActive ? ' font-medium text-text-primary' : ' text-text-primary'}`}>
                {m.name}
              </span>
              <span className={`mono text-[11px] ${durationColor}`}>
                {m.duration ?? '—'}
              </span>
            </div>
          )
        })}
      </div>

      <ConfirmDialog
        open={!!deleteId}
        title={t('annotations.deleteMedia')}
        onConfirm={handleDelete}
        onCancel={() => setDeleteId(null)}
      />
    </div>
  )
}
