'use client'

import { useEffect, useRef, useState, useTransition } from 'react'
import Link from 'next/link'
import { Camera, MapPin, Pencil, X, Check, Mic, Square, Play, Pause, Plus, Star, Images } from 'lucide-react'
import { LocationAutocomplete, type LocationSuggestion } from '@/components/location-autocomplete'
import { LocationMapPicker, type PickerMap } from '@/components/location-map-picker'
import { captureItem } from './actions'

interface Recent {
  itemId: string
  thumbDataUrl: string
  locationPath: string
}

const LOCATION_KEY = 'eorg.capture.location'

export function CaptureSession({
  initialLocation,
  suggestions,
  maps,
}: {
  initialLocation: string
  suggestions: LocationSuggestion[]
  maps: PickerMap[]
}) {
  const [location, setLocation] = useState(() => {
    if (initialLocation) return initialLocation
    if (typeof window === 'undefined') return ''
    return window.localStorage.getItem(LOCATION_KEY) || ''
  })
  const [editingLocation, setEditingLocation] = useState(() => {
    if (initialLocation) return false
    if (typeof window === 'undefined') return true
    return !window.localStorage.getItem(LOCATION_KEY)
  })
  // Multiple angles for one item. First entry becomes the primary photo
  // (the thumbnail used in lists). Latest entry is what's shown in the main
  // preview so the user sees the shot they just took.
  const [photos, setPhotos] = useState<{ file: File; previewUrl: string }[]>([])
  const [viewIndex, setViewIndex] = useState(0)
  const [caption, setCaption] = useState('')
  const [voiceBlob, setVoiceBlob] = useState<Blob | null>(null)
  const [voiceMime, setVoiceMime] = useState<string>('')
  const [voicePreview, setVoicePreview] = useState<string | null>(null)
  const [recording, setRecording] = useState(false)
  const [recordSeconds, setRecordSeconds] = useState(0)
  const [error, setError] = useState<string | null>(null)
  const [recent, setRecent] = useState<Recent[]>([])
  const [pending, startTransition] = useTransition()

  const photoInputRef = useRef<HTMLInputElement>(null)
  const photoLibraryInputRef = useRef<HTMLInputElement>(null)
  const captionRef = useRef<HTMLTextAreaElement>(null)
  const recorderRef = useRef<MediaRecorder | null>(null)
  const chunksRef = useRef<BlobPart[]>([])
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
  const audioRef = useRef<HTMLAudioElement | null>(null)

  // Persist the pinned location so navigating to /identify and back keeps it.
  useEffect(() => {
    if (location && typeof window !== 'undefined') {
      window.localStorage.setItem(LOCATION_KEY, location)
    }
  }, [location])

  useEffect(() => {
    return () => {
      for (const p of photos) URL.revokeObjectURL(p.previewUrl)
      if (voicePreview) URL.revokeObjectURL(voicePreview)
      if (timerRef.current) clearInterval(timerRef.current)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const pickPhoto = () => {
    if (!location || editingLocation) {
      setError('Set a location first')
      setEditingLocation(true)
      return
    }
    photoInputRef.current?.click()
  }

  const pickFromLibrary = () => {
    if (!location || editingLocation) {
      setError('Set a location first')
      setEditingLocation(true)
      return
    }
    photoLibraryInputRef.current?.click()
  }

  const onPhotoChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    // The camera input yields one shot; the library input allows multi-select.
    const files = Array.from(e.target.files ?? [])
    // Reset the input so re-picking the same file still fires onChange.
    e.target.value = ''
    if (files.length === 0) return
    setPhotos(prev => {
      const next = [
        ...prev,
        ...files.map(file => ({ file, previewUrl: URL.createObjectURL(file) })),
      ]
      setViewIndex(next.length - 1)
      return next
    })
    setError(null)
  }

  const removePhotoAt = (idx: number) => {
    setPhotos(prev => {
      const target = prev[idx]
      if (target) URL.revokeObjectURL(target.previewUrl)
      const next = prev.filter((_, i) => i !== idx)
      setViewIndex(v => Math.max(0, Math.min(v, next.length - 1)))
      return next
    })
  }

  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 reset = () => {
    for (const p of photos) URL.revokeObjectURL(p.previewUrl)
    setPhotos([])
    setViewIndex(0)
    setCaption('')
    clearVoice()
  }

  const save = () => {
    if (photos.length === 0) return
    if (!location) {
      setError('Set a location first')
      return
    }
    const fd = new FormData()
    fd.set('location', location)
    // Order matters — first entry is treated as primary on the server.
    for (const p of photos) fd.append('photo', p.file)
    if (caption.trim()) fd.set('caption', caption.trim())
    if (voiceBlob) {
      const ext = voiceMime.includes('mp4') ? 'm4a' : voiceMime.includes('ogg') ? 'ogg' : 'webm'
      fd.set('voice', voiceBlob, `voice.${ext}`)
      fd.set('voiceMime', voiceMime)
    }

    const primaryThumb = photos[0]?.previewUrl ?? ''

    startTransition(async () => {
      const res = await captureItem(fd)
      if (!res.ok) {
        setError(res.error)
        return
      }
      setRecent(prev => [
        { itemId: res.itemId, thumbDataUrl: primaryThumb, locationPath: res.locationPath },
        ...prev.slice(0, 4),
      ])
      // Revoke non-primary previews; keep the primary alive — it's now
      // referenced by the "Just saved" strip.
      for (let i = 1; i < photos.length; i++) URL.revokeObjectURL(photos[i].previewUrl)
      setPhotos([])
      setViewIndex(0)
      setCaption('')
      clearVoice()
      setError(null)
      setTimeout(() => photoInputRef.current?.click(), 100)
    })
  }

  return (
    <div className="space-y-4">
      {error && (
        <div className="bg-destructive/10 text-destructive text-sm rounded-lg px-3 py-2 border border-destructive/20">
          {error}
        </div>
      )}

      <LocationChip
        value={location}
        editing={editingLocation}
        onChange={setLocation}
        onEdit={() => setEditingLocation(true)}
        onCommit={() => {
          if (location.trim()) {
            setEditingLocation(false)
            setError(null)
          }
        }}
        suggestions={suggestions}
        maps={maps}
      />

      {photos.length > 0 ? (
        <div className="space-y-3">
          <div className="relative">
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={photos[viewIndex]?.previewUrl}
              alt="Captured"
              className="w-full max-h-[55vh] object-contain rounded-xl bg-muted"
            />
            <button
              type="button"
              onClick={reset}
              aria-label="Discard all photos"
              className="absolute top-2 right-2 size-9 rounded-full bg-black/60 text-white flex items-center justify-center"
            >
              <X className="size-4" />
            </button>
          </div>

          <div className="flex gap-2 overflow-x-auto pb-1 -mx-1 px-1">
            {photos.map((p, idx) => (
              <div
                key={p.previewUrl}
                className={
                  'relative shrink-0 size-16 rounded-lg overflow-hidden border-2 ' +
                  (idx === viewIndex ? 'border-primary' : 'border-transparent')
                }
              >
                <button
                  type="button"
                  onClick={() => setViewIndex(idx)}
                  className="block size-full"
                  aria-label={`View angle ${idx + 1}`}
                >
                  {/* eslint-disable-next-line @next/next/no-img-element */}
                  <img src={p.previewUrl} alt="" className="size-full object-cover bg-muted" />
                </button>
                {idx === 0 && (
                  <span
                    className="absolute bottom-0 left-0 px-1 py-0.5 text-[10px] bg-primary text-primary-foreground rounded-tr"
                    title="Primary photo (shown in lists)"
                  >
                    <Star className="size-3 inline-block fill-current" />
                  </span>
                )}
                <button
                  type="button"
                  onClick={() => removePhotoAt(idx)}
                  aria-label={`Remove angle ${idx + 1}`}
                  className="absolute top-0 right-0 size-5 rounded-bl bg-black/60 text-white flex items-center justify-center"
                >
                  <X className="size-3" />
                </button>
              </div>
            ))}
            <button
              type="button"
              onClick={pickPhoto}
              className="shrink-0 size-16 rounded-lg border-2 border-dashed flex flex-col items-center justify-center text-muted-foreground hover:bg-accent/40"
              aria-label="Add another angle"
            >
              <Plus className="size-5" />
              <span className="text-[10px] leading-none mt-0.5">Angle</span>
            </button>
            <button
              type="button"
              onClick={pickFromLibrary}
              className="shrink-0 size-16 rounded-lg border-2 border-dashed flex flex-col items-center justify-center text-muted-foreground hover:bg-accent/40"
              aria-label="Add from photo library"
            >
              <Images className="size-5" />
              <span className="text-[10px] leading-none mt-0.5">Library</span>
            </button>
          </div>

          <VoicePanel
            recording={recording}
            seconds={recordSeconds}
            voicePreview={voicePreview}
            onStart={startRecording}
            onStop={stopRecording}
            onClear={clearVoice}
            audioRef={audioRef}
          />

          <div className="space-y-1.5">
            <label className="text-sm font-medium">Caption (optional)</label>
            <textarea
              ref={captionRef}
              value={caption}
              onChange={e => setCaption(e.target.value)}
              placeholder="Type, or tap the keyboard mic to dictate"
              rows={2}
              className="flex w-full min-h-[3rem] 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"
            />
            <p className="text-xs text-muted-foreground">
              Voice notes are transcribed at identify time and merged into the caption.
            </p>
          </div>

          <button
            type="button"
            onClick={save}
            disabled={pending || recording}
            className="w-full h-14 rounded-xl bg-primary text-primary-foreground text-base font-medium inline-flex items-center justify-center gap-2 disabled:opacity-50"
          >
            <Check className="size-5" />
            {pending ? 'Saving…' : 'Save & next'}
          </button>
        </div>
      ) : (
        <div className="space-y-2">
          <button
            type="button"
            onClick={pickPhoto}
            className="w-full aspect-[4/3] rounded-2xl border-2 border-dashed flex flex-col items-center justify-center gap-3 text-muted-foreground hover:bg-accent/40 transition-colors"
          >
            <Camera className="size-12" />
            <span className="font-medium text-base">Tap to capture</span>
            <span className="text-xs">photo → voice/caption → save → next</span>
          </button>
          <button
            type="button"
            onClick={pickFromLibrary}
            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"
          >
            <Images className="size-4" />
            Choose from photo library
          </button>
        </div>
      )}

      <input
        ref={photoInputRef}
        type="file"
        accept="image/*"
        capture="environment"
        className="sr-only"
        onChange={onPhotoChange}
      />
      <input
        ref={photoLibraryInputRef}
        type="file"
        accept="image/*"
        multiple
        className="sr-only"
        onChange={onPhotoChange}
      />

      {recent.length > 0 && (
        <div>
          <div className="text-xs uppercase tracking-wide text-muted-foreground mb-2">Just saved</div>
          <div className="flex gap-2 overflow-x-auto pb-2">
            {recent.map(r => (
              <Link key={r.itemId} href={`/items/${r.itemId}`} className="shrink-0 w-20 text-center">
                {r.thumbDataUrl && (
                  /* eslint-disable-next-line @next/next/no-img-element */
                  <img src={r.thumbDataUrl} alt="" className="size-20 rounded-lg object-cover bg-muted" />
                )}
                <div className="text-[10px] text-muted-foreground mt-1 truncate">{r.locationPath}</div>
              </Link>
            ))}
          </div>
          <div className="text-xs text-muted-foreground">
            Clean up names later from{' '}
            <Link href="/identify" className="underline">Identify</Link>.
          </div>
        </div>
      )}
    </div>
  )
}

function LocationChip({
  value,
  editing,
  onChange,
  onEdit,
  onCommit,
  suggestions,
  maps,
}: {
  value: string
  editing: boolean
  onChange: (v: string) => void
  onEdit: () => void
  onCommit: () => void
  suggestions: LocationSuggestion[]
  maps: PickerMap[]
}) {
  if (!editing) {
    return (
      <button
        type="button"
        onClick={onEdit}
        className="w-full flex items-center gap-2 p-3 rounded-xl border bg-card text-left hover:bg-accent/40"
      >
        <MapPin className="size-4 text-primary shrink-0" />
        <span className="flex-1 font-medium truncate">{value || 'Set a location'}</span>
        <Pencil className="size-4 text-muted-foreground" />
      </button>
    )
  }

  return (
    <div className="space-y-2">
      <div className="space-y-1.5">
        <label className="text-sm font-medium">Location</label>
        <div className="flex gap-2">
          <div className="flex-1 min-w-0">
            <LocationAutocomplete
              value={value}
              onChange={onChange}
              onCommit={onCommit}
              suggestions={suggestions}
              placeholder="Garage - middle shelf - 4th level"
              autoFocus
            />
          </div>
          <LocationMapPicker
            maps={maps}
            onPick={(_id, path) => onChange(path)}
            trigger={
              <button
                type="button"
                className="h-11 px-3 rounded-lg border bg-card text-sm font-medium inline-flex items-center gap-1.5 hover:bg-accent/40 shrink-0"
                title="Pick on a map"
              >
                <MapPin className="size-4" />
                Map
              </button>
            }
          />
        </div>
        <p className="text-xs text-muted-foreground">
          Type — use <code className="px-1 rounded bg-muted">{'>'}</code>,{' '}
          <code className="px-1 rounded bg-muted">/</code>, or{' '}
          <code className="px-1 rounded bg-muted">{' - '}</code> to nest (missing
          levels auto-created) — or tap <strong>Map</strong> to pick a pin.
        </p>
      </div>
      <button
        type="button"
        onClick={onCommit}
        disabled={!value.trim()}
        className="w-full h-11 rounded-lg bg-primary text-primary-foreground text-sm font-medium disabled:opacity-50"
      >
        Pin location
      </button>
    </div>
  )
}

function VoicePanel({
  recording,
  seconds,
  voicePreview,
  onStart,
  onStop,
  onClear,
  audioRef,
}: {
  recording: boolean
  seconds: number
  voicePreview: string | null
  onStart: () => void
  onStop: () => void
  onClear: () => void
  audioRef: React.RefObject<HTMLAudioElement | null>
}) {
  const [playing, setPlaying] = useState(false)

  const toggle = () => {
    const el = audioRef.current
    if (!el) return
    if (el.paused) {
      el.play()
      setPlaying(true)
    } else {
      el.pause()
      setPlaying(false)
    }
  }

  if (voicePreview) {
    return (
      <div className="flex items-center gap-2 p-3 rounded-xl border bg-card">
        <button
          type="button"
          onClick={toggle}
          className="size-10 rounded-full bg-primary text-primary-foreground flex items-center justify-center"
          aria-label={playing ? 'Pause' : 'Play'}
        >
          {playing ? <Pause className="size-4" /> : <Play className="size-4 translate-x-[1px]" />}
        </button>
        <audio
          ref={audioRef}
          src={voicePreview}
          onEnded={() => setPlaying(false)}
          onPause={() => setPlaying(false)}
          className="hidden"
        />
        <div className="flex-1 text-sm">
          <div className="font-medium">Voice note recorded</div>
          <div className="text-xs text-muted-foreground">Transcribed at identify time</div>
        </div>
        <button
          type="button"
          onClick={onClear}
          className="size-9 rounded-full border bg-card text-muted-foreground flex items-center justify-center"
          aria-label="Discard voice note"
        >
          <X className="size-4" />
        </button>
      </div>
    )
  }

  if (recording) {
    return (
      <button
        type="button"
        onClick={onStop}
        className="w-full h-14 rounded-xl bg-destructive text-destructive-foreground text-base font-medium inline-flex items-center justify-center gap-2"
      >
        <Square className="size-5 fill-current" />
        Stop recording · {formatSeconds(seconds)}
      </button>
    )
  }

  return (
    <button
      type="button"
      onClick={onStart}
      className="w-full h-12 rounded-xl border bg-card text-base font-medium inline-flex items-center justify-center gap-2 hover:bg-accent/40"
    >
      <Mic className="size-5 text-primary" />
      Record voice note (optional)
    </button>
  )
}

function formatSeconds(s: number) {
  const m = Math.floor(s / 60)
  const r = s % 60
  return `${m}:${String(r).padStart(2, '0')}`
}
