'use client'

import { useEffect, useRef, useState, useTransition } from 'react'
import { Sparkles, Mic, Square, Loader2, AlertTriangle, CheckCircle2, X } from 'lucide-react'
import { formatDistanceToNow } from 'date-fns'
import { ItemRow } from '@/components/item-row'
import type { SearchResult } from '@/lib/search'
import type { AISearchSessionRow } from '@/lib/ai-search-session'
import {
  startAISearchAction,
  fetchAISearchSession,
  loadResultItems,
} from './actions'

interface Props {
  initialSession: AISearchSessionRow | null
  initialResults: SearchResult[]
}

export function AISearchForm({ initialSession, initialResults }: Props) {
  const [query, setQuery] = useState('')
  const [session, setSession] = useState<AISearchSessionRow | null>(initialSession)
  const [results, setResults] = useState<SearchResult[]>(initialResults)
  const [error, setError] = useState<string | null>(null)
  const [pending, startTransition] = useTransition()

  const [recording, setRecording] = useState(false)
  const [recordSeconds, setRecordSeconds] = useState(0)
  const [voiceBlob, setVoiceBlob] = useState<Blob | null>(null)
  const [voiceMime, setVoiceMime] = useState<string>('')
  const [voicePreview, setVoicePreview] = useState<string | null>(null)
  const recorderRef = useRef<MediaRecorder | null>(null)
  const chunksRef = useRef<Blob[]>([])
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
  const pollRef = useRef<ReturnType<typeof setInterval> | null>(null)

  const isRunning =
    session?.status === 'PENDING' || session?.status === 'RUNNING'

  useEffect(() => {
    return () => {
      if (timerRef.current) clearInterval(timerRef.current)
      if (voicePreview) URL.revokeObjectURL(voicePreview)
      recorderRef.current?.stream?.getTracks().forEach(t => t.stop())
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  // Poll the running session every 3s. Stop on terminal status. Load result
  // items once we hit SUCCEEDED.
  useEffect(() => {
    if (!session || !isRunning) {
      if (pollRef.current) {
        clearInterval(pollRef.current)
        pollRef.current = null
      }
      return
    }
    const id = session.id
    pollRef.current = setInterval(async () => {
      const next = await fetchAISearchSession(id)
      if (!next) return
      setSession(next)
      if (next.status === 'SUCCEEDED' && next.resultItemIds.length > 0) {
        const items = await loadResultItems(next.resultItemIds)
        setResults(items)
      }
    }, 3000)
    return () => {
      if (pollRef.current) clearInterval(pollRef.current)
      pollRef.current = null
    }
  }, [session, isRunning])

  const startRecording = async () => {
    setError(null)
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
      const mimePrefs = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4', 'audio/ogg;codecs=opus']
      const mime = mimePrefs.find(m => MediaRecorder.isTypeSupported(m)) || ''
      const recorder = mime ? new MediaRecorder(stream, { mimeType: mime }) : new MediaRecorder(stream)
      chunksRef.current = []
      recorder.ondataavailable = e => {
        if (e.data && e.data.size > 0) chunksRef.current.push(e.data)
      }
      recorder.onstop = () => {
        const blob = new Blob(chunksRef.current, { type: recorder.mimeType })
        if (voicePreview) URL.revokeObjectURL(voicePreview)
        setVoiceBlob(blob)
        setVoiceMime(recorder.mimeType || 'audio/webm')
        setVoicePreview(URL.createObjectURL(blob))
        stream.getTracks().forEach(t => t.stop())
      }
      recorder.start()
      recorderRef.current = recorder
      setRecording(true)
      setRecordSeconds(0)
      if (timerRef.current) clearInterval(timerRef.current)
      timerRef.current = setInterval(() => setRecordSeconds(s => s + 1), 1000)
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Microphone unavailable')
    }
  }

  const stopRecording = () => {
    recorderRef.current?.stop()
    setRecording(false)
    if (timerRef.current) clearInterval(timerRef.current)
  }

  const clearVoice = () => {
    if (voicePreview) URL.revokeObjectURL(voicePreview)
    setVoiceBlob(null)
    setVoicePreview(null)
    setVoiceMime('')
  }

  const onSubmit = () => {
    if (recording) {
      setError('Stop recording first.')
      return
    }
    if (!query.trim() && !voiceBlob) {
      setError('Describe what you’re looking for, or record a voice clip.')
      return
    }
    setError(null)

    const fd = new FormData()
    if (query.trim()) fd.set('query', query.trim())
    if (voiceBlob) {
      const ext = voiceMime.includes('mp4') ? 'm4a' : voiceMime.includes('ogg') ? 'ogg' : 'webm'
      fd.set('audio', voiceBlob, `query.${ext}`)
      fd.set('audioMime', voiceMime)
    }

    startTransition(async () => {
      const res = await startAISearchAction(fd)
      if (!res.ok) {
        setError(res.error)
        return
      }
      const next = await fetchAISearchSession(res.sessionId)
      setSession(next)
      setResults([])
      setQuery('')
      clearVoice()
    })
  }

  return (
    <div className="space-y-4">
      <div className="rounded-xl border bg-card p-3 space-y-3">
        <div className="flex items-start gap-2">
          <Sparkles className="size-5 text-primary mt-1" />
          <div className="text-xs text-muted-foreground leading-snug">
            Describe what you’re looking for. The home server reads your
            inventory and returns matching items. Expect ~1–2 minutes; voice
            input is transcribed locally.
          </div>
        </div>

        <textarea
          value={query}
          onChange={e => setQuery(e.target.value)}
          placeholder="e.g. the thing I use to trim hedges, or the box of holiday lights"
          rows={3}
          disabled={isRunning || pending}
          className="flex w-full min-h-[5rem] rounded-lg border border-input bg-background px-3 py-2 text-base focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-60"
        />

        {/* Voice block */}
        {voicePreview ? (
          <div className="flex items-center gap-2 rounded-lg border bg-muted/30 p-2">
            <audio src={voicePreview} controls className="flex-1 h-8" />
            <button
              type="button"
              onClick={clearVoice}
              className="size-8 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent/40 inline-flex items-center justify-center"
              aria-label="Clear voice"
            >
              <X className="size-4" />
            </button>
          </div>
        ) : recording ? (
          <button
            type="button"
            onClick={stopRecording}
            className="w-full h-10 rounded-lg bg-destructive/10 border border-destructive/30 text-destructive text-sm font-medium inline-flex items-center justify-center gap-2"
          >
            <Square className="size-4" />
            Stop ({recordSeconds}s)
          </button>
        ) : (
          <button
            type="button"
            onClick={startRecording}
            disabled={isRunning || pending}
            className="w-full h-10 rounded-lg border text-sm text-muted-foreground inline-flex items-center justify-center gap-2 hover:bg-accent/40 disabled:opacity-60"
          >
            <Mic className="size-4" />
            Record voice description
          </button>
        )}

        <button
          type="button"
          onClick={onSubmit}
          disabled={isRunning || pending || recording}
          className="w-full h-11 rounded-lg bg-primary text-primary-foreground font-medium text-sm inline-flex items-center justify-center gap-2 disabled:opacity-50"
        >
          {pending || isRunning ? (
            <>
              <Loader2 className="size-4 animate-spin" />
              {pending ? 'Starting…' : statusLabel(session!.status)}
            </>
          ) : (
            <>
              <Sparkles className="size-4" />
              Search with AI
            </>
          )}
        </button>

        {error && (
          <div className="text-xs text-destructive bg-destructive/10 rounded px-2 py-1.5 flex items-start gap-2">
            <AlertTriangle className="size-3.5 mt-0.5 shrink-0" />
            <span>{error}</span>
          </div>
        )}
      </div>

      {session && <SessionPanel session={session} />}

      {results.length > 0 && (
        <div className="space-y-2">
          <div className="text-xs uppercase tracking-wide text-muted-foreground">
            Matches ({results.length})
          </div>
          {results.map(item => (
            <ItemRow key={item.id} item={item} />
          ))}
        </div>
      )}

      {session?.status === 'SUCCEEDED' && results.length === 0 && (
        <div className="text-center text-muted-foreground text-sm py-6">
          No matches found.
        </div>
      )}
    </div>
  )
}

function SessionPanel({ session }: { session: AISearchSessionRow }) {
  const startedAt = new Date(session.startedAt)
  const finishedAt = session.finishedAt ? new Date(session.finishedAt) : null
  return (
    <div className="rounded-xl border bg-card p-3 text-xs space-y-1.5">
      <div className="flex items-center gap-2 text-muted-foreground">
        <StatusIcon status={session.status} />
        <span>
          {statusLabel(session.status)}
          {' · '}
          {finishedAt
            ? `finished ${formatDistanceToNow(finishedAt, { addSuffix: true })}`
            : `started ${formatDistanceToNow(startedAt, { addSuffix: true })}`}
        </span>
      </div>
      {session.transcript && (
        <div className="text-foreground/80 italic">“{session.transcript}”</div>
      )}
      {session.reasoning && (
        <div className="text-muted-foreground">{session.reasoning}</div>
      )}
      {session.errorMessage && (
        <div className="text-destructive break-words">{session.errorMessage}</div>
      )}
      {session.logTail && session.status === 'FAILED' && (
        <details className="text-muted-foreground">
          <summary className="cursor-pointer select-none">Log tail</summary>
          <pre className="mt-1 max-h-48 overflow-auto whitespace-pre-wrap rounded bg-muted/40 p-2 text-[11px] leading-snug">
            {session.logTail}
          </pre>
        </details>
      )}
    </div>
  )
}

function StatusIcon({ status }: { status: AISearchSessionRow['status'] }) {
  if (status === 'PENDING' || status === 'RUNNING')
    return <Loader2 className="size-3.5 animate-spin text-primary" />
  if (status === 'SUCCEEDED') return <CheckCircle2 className="size-3.5 text-emerald-600" />
  return <AlertTriangle className="size-3.5 text-destructive" />
}

function statusLabel(status: AISearchSessionRow['status']): string {
  switch (status) {
    case 'PENDING': return 'Launching…'
    case 'RUNNING': return 'Searching on home server'
    case 'SUCCEEDED': return 'Done'
    case 'FAILED': return 'Failed'
  }
}
