import { useState, useRef, useEffect, useCallback } from 'react'
import { fetchAPI, postAPI } from '../api/client'
import { useToast } from '../components/Toast'
import ErrorBanner from '../components/ErrorBanner'

interface MediaItem {
  id: number
  media_type: string
  title: string
  year: number | null
  status: string
  monitored: boolean
}

interface SearchResult {
  title: string
  guid: string
  size: number
  pub_date: string
  indexer_name: string
  indexer_priority: number
  quality: {
    title: string
    resolution: string
    source: string
    video_codec: string
    release_group: string
    parse_warning: boolean
  }
  quality_tier: {
    name: string
    rank: number
    resolution: string
  } | null
  seeders: number
  peers: number
  category: string
  download_url: string
  source_indexers: string[]
}

type SortCol = 'quality' | 'size' | 'seeders' | 'age'
type SortDir = 'asc' | 'desc'

function formatFileSize(bytes: number): string {
  if (bytes >= 1024 * 1024 * 1024) {
    return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`
  }
  if (bytes >= 1024 * 1024) {
    return `${(bytes / 1024 / 1024).toFixed(1)} MB`
  }
  return `${(bytes / 1024).toFixed(0)} KB`
}

function formatAge(dateStr: string): string {
  const diff = Date.now() - new Date(dateStr).getTime()
  const minutes = Math.floor(diff / 60000)
  if (minutes < 1) return 'now'
  if (minutes < 60) return `${minutes}m`
  const hours = Math.floor(minutes / 60)
  if (hours < 24) return `${hours}h`
  const days = Math.floor(hours / 24)
  if (days < 30) return `${days}d`
  const months = Math.floor(days / 30)
  return `${months}mo`
}

function extractSearchHint(title: string): string {
  // Take the first few meaningful words from a release title as a media search hint
  const cleaned = title.replace(/[.\-]+/g, ' ').trim()
  const words = cleaned.split(/\s+/)
  // Take up to 3 words, but stop at common quality/year markers
  const stopWords = new Set(['720p', '1080p', '2160p', '4k', 'bluray', 'webrip', 'webdl', 'hdtv', 'dvdrip', 'x264', 'x265', 'hevc', 'aac', 'dts'])
  const hint: string[] = []
  for (const word of words.slice(0, 6)) {
    if (stopWords.has(word.toLowerCase())) break
    if (/^\d{4}$/.test(word)) break
    hint.push(word)
    if (hint.length >= 3) break
  }
  return hint.join(' ') || title.slice(0, 30)
}

export default function Search() {
  // Search state
  const [query, setQuery] = useState('')
  const [results, setResults] = useState<SearchResult[]>([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [hasSearched, setHasSearched] = useState(false)

  // Sorting
  const [sortCol, setSortCol] = useState<SortCol>('quality')
  const [sortDir, setSortDir] = useState<SortDir>('asc')

  // Media selector modal state
  const [modalOpen, setModalOpen] = useState(false)
  const [pendingResult, setPendingResult] = useState<SearchResult | null>(null)
  const [mediaQuery, setMediaQuery] = useState('')
  const [mediaItems, setMediaItems] = useState<MediaItem[]>([])
  const [mediaLoading, setMediaLoading] = useState(false)

  // Grab tracking
  const [grabbing, setGrabbing] = useState<Set<string>>(new Set())

  const { showToast } = useToast()
  const mediaSearchRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const modalInputRef = useRef<HTMLInputElement>(null)

  function handleSort(col: SortCol) {
    if (sortCol === col) {
      setSortDir(prev => (prev === 'asc' ? 'desc' : 'asc'))
    } else {
      setSortCol(col)
      setSortDir('asc')
    }
  }

  const sorted = [...results].sort((a, b) => {
    let cmp = 0
    switch (sortCol) {
      case 'quality':
        cmp = (a.quality_tier?.rank ?? 999) - (b.quality_tier?.rank ?? 999)
        break
      case 'size':
        cmp = a.size - b.size
        break
      case 'seeders':
        cmp = a.seeders - b.seeders
        break
      case 'age':
        cmp = new Date(a.pub_date).getTime() - new Date(b.pub_date).getTime()
        break
    }
    return sortDir === 'asc' ? cmp : -cmp
  })

  async function doSearch() {
    const trimmed = query.trim()
    if (!trimmed) return
    setLoading(true)
    setHasSearched(true)
    setError(null)
    try {
      const res = await fetchAPI<{ data: SearchResult[]; total: number }>(
        '/api/releases/search?query=' + encodeURIComponent(trimmed)
      )
      setResults(res.data ?? [])
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Search failed'
      setError(message)
    } finally {
      setLoading(false)
    }
  }

  function handleSearchSubmit(e: React.FormEvent) {
    e.preventDefault()
    doSearch()
  }

  // Media selector: open modal
  function openMediaSelector(result: SearchResult) {
    setPendingResult(result)
    const hint = extractSearchHint(result.title)
    setMediaQuery(hint)
    setMediaItems([])
    setModalOpen(true)
  }

  // Fetch media items when modal query changes (debounced)
  const fetchMedia = useCallback(async (q: string) => {
    if (!q.trim()) {
      setMediaItems([])
      return
    }
    setMediaLoading(true)
    try {
      const res = await fetchAPI<{ data: MediaItem[]; total: number }>(
        '/api/search?q=' + encodeURIComponent(q) + '&page_size=20'
      )
      setMediaItems(res.data ?? [])
    } catch {
      setMediaItems([])
    } finally {
      setMediaLoading(false)
    }
  }, [])

  // Debounce media search input
  useEffect(() => {
    if (!modalOpen) return
    if (mediaSearchRef.current) clearTimeout(mediaSearchRef.current)
    mediaSearchRef.current = setTimeout(() => {
      fetchMedia(mediaQuery)
    }, 300)
    return () => {
      if (mediaSearchRef.current) clearTimeout(mediaSearchRef.current)
    }
  }, [mediaQuery, modalOpen, fetchMedia])

  // Focus modal input when modal opens
  useEffect(() => {
    if (modalOpen) {
      // Small delay to let modal render
      setTimeout(() => modalInputRef.current?.focus(), 50)
    }
  }, [modalOpen])

  async function handleGrabWithMedia(item: MediaItem) {
    if (!pendingResult) return
    setGrabbing(prev => new Set(prev).add(pendingResult.guid))
    try {
      await postAPI<{ queue_id: number }>('/api/releases/grab', {
        download_url: pendingResult.download_url,
        title: pendingResult.title,
        media_type: item.media_type,
        quality: pendingResult.quality,
        indexer_name: pendingResult.indexer_name,
        media_id: item.id,
      })
      showToast(`✓ Grabbed "${pendingResult.title}"`)
      setModalOpen(false)
      setPendingResult(null)
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error'
      showToast(`✗ Failed to grab: ${message}`)
    } finally {
      if (pendingResult) {
        setGrabbing(prev => {
          const next = new Set(prev)
          next.delete(pendingResult.guid)
          return next
        })
      }
    }
  }

  function closeModal() {
    setModalOpen(false)
    setPendingResult(null)
    setMediaItems([])
    setMediaQuery('')
  }

  function SortHeader({ col, label }: { col: SortCol; label: string }) {
    const active = sortCol === col
    return (
      <button
        onClick={() => handleSort(col)}
        className={`text-left text-xs font-semibold uppercase tracking-wide transition-colors ${
          active ? 'text-indigo-400 hover:text-indigo-300' : 'text-gray-400 hover:text-gray-200'
        }`}
      >
        {label} {active && (sortDir === 'asc' ? '▲' : '▼')}
      </button>
    )
  }

  return (
    <div>
      <h2 className="text-2xl font-semibold text-gray-100 mb-6">Search Indexers</h2>

      {/* Search input */}
      <form onSubmit={handleSearchSubmit} className="mb-6">
        <div className="flex gap-3 max-w-2xl">
          <input
            type="text"
            value={query}
            onChange={e => setQuery(e.target.value)}
            placeholder="Search all indexers..."
            className="bg-gray-800 border border-gray-700 focus:border-indigo-500 text-white rounded-lg px-4 py-3 w-full outline-none transition-colors"
          />
          <button
            type="submit"
            disabled={loading || !query.trim()}
            className="bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed text-white px-6 py-3 rounded-lg font-semibold transition-colors"
          >
            {loading ? 'Searching...' : 'Search'}
          </button>
        </div>
        <p className="text-gray-500 text-xs mt-2">Search across all enabled indexers for any release</p>
      </form>

      {/* Results area */}
      {!hasSearched && (
        <div className="text-center py-20 text-gray-500">
          <p className="text-lg">Enter a search query to find releases</p>
        </div>
      )}

      {hasSearched && loading && (
        <div className="animate-pulse space-y-3">
          <div className="h-10 bg-gray-800 rounded" />
          <div className="h-10 bg-gray-800 rounded" />
          <div className="h-10 bg-gray-800 rounded" />
        </div>
      )}

      {hasSearched && error && !loading && (
        <ErrorBanner error={error} onRetry={doSearch} />
      )}

      {hasSearched && !loading && !error && results.length === 0 && (
        <div className="text-gray-500 text-center py-12">No releases found</div>
      )}

      {hasSearched && !loading && !error && results.length > 0 && (
        <div>
          <p className="text-gray-400 text-sm mb-3">{results.length} release{results.length !== 1 ? 's' : ''} found</p>
          <div className="bg-gray-900 border border-gray-800 rounded-lg overflow-hidden">
            <table className="w-full">
              <thead>
                <tr className="border-b border-gray-800">
                  <th className="px-4 py-3"><SortHeader col="quality" label="Quality" /></th>
                  <th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wide">Title</th>
                  <th className="px-4 py-3"><SortHeader col="size" label="Size" /></th>
                  <th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wide">Indexer</th>
                  <th className="px-4 py-3"><SortHeader col="seeders" label="Seeders" /></th>
                  <th className="px-4 py-3"><SortHeader col="age" label="Age" /></th>
                  <th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wide w-28">Action</th>
                </tr>
              </thead>
              <tbody>
                {sorted.map(result => {
                  const isGrabbing = grabbing.has(result.guid)
                  const noUrl = !result.download_url
                  return (
                    <tr key={result.guid} className="border-b border-gray-800/50 hover:bg-gray-800/30">
                      <td className="px-4 py-3 text-sm">
                        {result.quality_tier ? (
                          <span className="text-gray-200">{result.quality_tier.name}</span>
                        ) : result.quality.resolution || result.quality.source ? (
                          <span className="text-gray-200">{result.quality.resolution} {result.quality.source}</span>
                        ) : (
                          <span className="text-gray-500">Unknown</span>
                        )}
                      </td>
                      <td className="px-4 py-3 text-sm text-gray-200 max-w-lg truncate" title={result.title}>
                        {result.title}
                      </td>
                      <td className="px-4 py-3 text-sm text-gray-400">{formatFileSize(result.size)}</td>
                      <td className="px-4 py-3 text-sm text-gray-400">{result.indexer_name}</td>
                      <td className="px-4 py-3 text-sm text-gray-400">
                        <span className={result.seeders > 0 ? 'text-green-400' : ''}>{result.seeders}</span>
                        {' / '}
                        <span>{result.peers}</span>
                      </td>
                      <td className="px-4 py-3 text-sm text-gray-400">{formatAge(result.pub_date)}</td>
                      <td className="px-4 py-3">
                        <button
                          disabled={isGrabbing || noUrl}
                          onClick={() => openMediaSelector(result)}
                          className="px-3 py-1 text-xs font-semibold rounded bg-indigo-600 hover:bg-indigo-500 text-white disabled:opacity-50 disabled:cursor-not-allowed"
                        >
                          {isGrabbing ? 'Grabbing...' : noUrl ? 'N/A' : 'Select & Grab'}
                        </button>
                      </td>
                    </tr>
                  )
                })}
              </tbody>
            </table>
          </div>
        </div>
      )}

      {/* Media selector modal */}
      {modalOpen && pendingResult && (
        <div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50" onClick={closeModal}>
          <div className="bg-gray-900 border border-gray-800 rounded-lg p-6 max-w-lg w-full mx-4" onClick={e => e.stopPropagation()}>
            <h3 className="text-lg font-semibold text-gray-100 mb-1">Select Media Item</h3>
            <p className="text-sm text-gray-400 mb-4">
              Associate "<span className="text-gray-200">{pendingResult.title}</span>" with a media item in your library
            </p>

            {/* Media search input */}
            <input
              ref={modalInputRef}
              type="text"
              value={mediaQuery}
              onChange={e => setMediaQuery(e.target.value)}
              placeholder="Search your library..."
              className="bg-gray-800 border border-gray-700 focus:border-indigo-500 text-white rounded-lg px-4 py-2.5 w-full mb-3 text-sm outline-none transition-colors"
            />

            {/* Media items list */}
            <div className="max-h-64 overflow-y-auto space-y-1">
              {mediaLoading && mediaItems.length === 0 && (
                <div className="text-gray-500 text-sm text-center py-6">Searching library...</div>
              )}
              {!mediaLoading && mediaItems.length === 0 && mediaQuery.trim() && (
                <div className="text-gray-500 text-sm text-center py-6">No matching media items found</div>
              )}
              {mediaItems.map(item => {
                const isGrabbingItem = grabbing.has(pendingResult.guid)
                return (
                  <button
                    key={item.id}
                    disabled={isGrabbingItem}
                    onClick={() => handleGrabWithMedia(item)}
                    className="w-full text-left px-3 py-2.5 rounded hover:bg-gray-800 transition-colors disabled:opacity-50"
                  >
                    <span className="text-sm text-gray-200">{item.title}</span>
                    <span className="text-xs text-gray-500 ml-2">
                      {item.media_type}{item.year ? ` · ${item.year}` : ''}
                    </span>
                  </button>
                )
              })}
            </div>

            {/* Modal actions */}
            <div className="flex justify-end gap-3 mt-4 pt-3 border-t border-gray-800">
              <button
                onClick={closeModal}
                className="px-4 py-2 text-sm bg-gray-700 hover:bg-gray-600 rounded text-gray-200 transition-colors"
              >
                Cancel
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}
