import { useEffect, useMemo, useRef, useState } from 'react'
import { cn } from '@/lib/cn'
import { fieldControlClass } from '@/components/ui/field'
import { CategoryChips } from '@/components/plants/CategoryChips'
import { PlantIcon } from '@/components/plants/PlantIcon'
import { CATEGORY_LABELS, filterPlants, usePlants, type CategoryFilter, type Plant } from '@/lib/plants'
import {
  attributableLots,
  formatQuantity,
  lotsByPlant,
  lotState,
  summarizeLots,
  useSeedLots,
  type SeedLot,
} from '@/lib/seedLots'
import { LotStateChip } from '@/components/plants/SeedLotList'
import { formatSpacing, type UnitPref } from '@/lib/units'

const RECENT_KEY = 'pansy:recent-plants'
const RECENT_MAX = 8

/** Recently-picked plant ids, most-recent first, from localStorage. */
function loadRecent(): number[] {
  try {
    const v: unknown = JSON.parse(localStorage.getItem(RECENT_KEY) ?? '[]')
    return Array.isArray(v) ? v.filter((n): n is number => typeof n === 'number') : []
  } catch {
    return []
  }
}

/** Prepend id (dedup, capped) and persist; returns the new list. */
function recordRecent(id: number): number[] {
  const next = [id, ...loadRecent().filter((n) => n !== id)].slice(0, RECENT_MAX)
  try {
    localStorage.setItem(RECENT_KEY, JSON.stringify(next))
  } catch {
    // Recents are a nicety; ignore quota/availability failures.
  }
  return next
}

/**
 * Reusable plant chooser: a search-first list with a recently-used shortcut and
 * category filter, rendered as a bottom sheet on mobile / centered panel on
 * desktop. `onSelect` fires with the chosen plant (and records it as recent).
 * The plops editor (#15) opens this when placing plants; /plants demos it.
 */
export function PlantPicker({
  onSelect,
  onClose,
  unit = 'metric',
}: {
  // The chosen plant, and the lot it should be attributed to when that isn't
  // ambiguous. Undefined lot means "don't attribute" — which is the honest
  // answer when there are no lots, and the deliberate one when the user skips.
  onSelect: (plant: Plant, lot?: SeedLot) => void
  onClose: () => void
  unit?: UnitPref
}) {
  const plants = usePlants()
  const seedLots = useSeedLots()
  const lotsFor = useMemo(() => lotsByPlant(seedLots.data), [seedLots.data])
  // Set when a plant with SEVERAL lots is picked: which packet did this come out
  // of? One lot auto-attributes and zero lots stays silent, because forcing that
  // question on every placement is how a nicety becomes an obstacle.
  const [choosingLotFor, setChoosingLotFor] = useState<Plant | null>(null)
  const [query, setQuery] = useState('')
  const [category, setCategory] = useState<CategoryFilter>('all')
  const [recent, setRecent] = useState<number[]>(() => loadRecent())
  const searchRef = useRef<HTMLInputElement>(null)
  const onCloseRef = useRef(onClose)
  onCloseRef.current = onClose

  useEffect(() => {
    searchRef.current?.focus()
    function onKey(e: KeyboardEvent) {
      if (e.key === 'Escape') onCloseRef.current()
    }
    document.addEventListener('keydown', onKey)
    return () => document.removeEventListener('keydown', onKey)
  }, [])

  const all = useMemo(() => plants.data ?? [], [plants.data])

  const filtered = useMemo(() => filterPlants(all, query, category), [all, query, category])

  // Recents show only when not actively searching, and only rows still present.
  const recentPlants = useMemo(() => {
    if (query.trim() !== '') return []
    const byId = new Map(all.map((p) => [p.id, p]))
    return recent.map((id) => byId.get(id)).filter((p): p is Plant => !!p)
  }, [all, recent, query])

  function choose(p: Plant) {
    const lots = attributableLots(lotsFor.get(p.id) ?? [])
    if (lots.length > 1) {
      setChoosingLotFor(p)
      return
    }
    setRecent(recordRecent(p.id))
    // A single lot attributes even when its count says empty. The count records
    // what was written down, not a fact about the packet — dropping attribution
    // there would make a wrong number harder to correct rather than easier.
    onSelect(p, lots[0])
  }

  function chooseLot(p: Plant, lot?: SeedLot) {
    setRecent(recordRecent(p.id))
    onSelect(p, lot)
  }

  // A plant option row. keyPrefix namespaces the key so a plant appearing in
  // both the Recent and All sections doesn't collide on a duplicate React key.
  const row = (p: Plant, keyPrefix: string) => (
    <button
      key={`${keyPrefix}-${p.id}`}
      type="button"
      onClick={() => choose(p)}
      className="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left outline-none transition-colors hover:bg-border/50 focus-visible:bg-border/50"
    >
      <PlantIcon color={p.color} icon={p.icon} className="h-9 w-9 rounded-md text-xl" />
      <span className="min-w-0 flex-1">
        <span className="block truncate font-medium text-fg">{p.name}</span>
        <span className="block text-xs text-muted">
          {CATEGORY_LABELS[p.category]} · {formatSpacing(p.spacingCm, unit)}
          {remainingLabel(lotsFor.get(p.id) ?? [])}
        </span>
      </span>
    </button>
  )

  // What's left, shown where you're deciding what to plant. Silent when there
  // are no lots — most plants won't have any, and "0 left" on all of them would
  // be noise that trains you to ignore the number. summarizeLots owns the
  // unit-agreement rule; a second copy of it here would drift.
  function remainingLabel(lots: SeedLot[]): string {
    if (lots.length === 0) return ''
    const { remaining, unit } = summarizeLots(lots)
    return ` · ${formatQuantity(remaining)}${unit ? ` ${unit}` : ''} left`
  }

  return (
    <div
      className="fixed inset-0 z-50 flex items-end justify-center bg-black/40 sm:items-center sm:p-4"
      onMouseDown={(e) => e.target === e.currentTarget && onClose()}
    >
      <div
        role="dialog"
        aria-modal="true"
        aria-label="Choose a plant"
        className="flex max-h-[85vh] w-full flex-col rounded-t-2xl border border-border bg-surface shadow-xl sm:max-w-lg sm:rounded-2xl"
      >
        <div className="flex items-center gap-2 border-b border-border p-3">
          <input
            ref={searchRef}
            type="search"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder="Search plants…"
            aria-label="Search plants"
            className={cn(fieldControlClass, 'min-w-0 flex-1')}
          />
          <button
            type="button"
            onClick={onClose}
            aria-label="Close"
            className="rounded-md px-2 py-2 text-muted outline-none transition-colors hover:bg-border/50 hover:text-fg focus-visible:ring-2 focus-visible:ring-accent/40"
          >
            ✕
          </button>
        </div>

        <div className="border-b border-border px-3 py-2">
          <CategoryChips value={category} onChange={setCategory} size="sm" />
        </div>

        {choosingLotFor && (
          <div className="min-h-0 flex-1 overflow-y-auto p-2">
            <p className="px-3 pb-1 pt-2 text-xs font-semibold uppercase tracking-wide text-muted">
              Which lot of {choosingLotFor.name}?
            </p>
            {attributableLots(lotsFor.get(choosingLotFor.id) ?? []).map((lot) => (
                <button
                  key={lot.id}
                  type="button"
                  onClick={() => chooseLot(choosingLotFor, lot)}
                  className="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left outline-none transition-colors hover:bg-border/50 focus-visible:bg-border/50"
                >
                  <span className="min-w-0 flex-1">
                    <span className="block truncate text-sm font-medium text-fg">
                      {lot.vendor || 'Unnamed lot'}
                      {lot.packedForYear != null ? ` · ${lot.packedForYear}` : ''}
                    </span>
                    <span className="block text-xs text-muted">
                      {formatQuantity(lot.remaining)} of {formatQuantity(lot.quantity)} {lot.unit} left
                    </span>
                  </span>
                  <LotStateChip state={lotState(lot)} />
                </button>
            ))}
            <button
              type="button"
              onClick={() => chooseLot(choosingLotFor, undefined)}
              className="w-full rounded-lg px-3 py-2.5 text-left text-sm text-muted outline-none transition-colors hover:bg-border/50 focus-visible:bg-border/50"
            >
              Don't attribute to a lot
            </button>
          </div>
        )}

        <div className={cn('min-h-0 flex-1 overflow-y-auto p-2', choosingLotFor && 'hidden')}>
          {plants.isPending && <p className="p-4 text-sm text-muted">Loading plants…</p>}
          {plants.isError && <p className="p-4 text-sm text-red-600 dark:text-red-400">Couldn't load plants.</p>}

          {recentPlants.length > 0 && (
            <>
              <p className="px-3 pb-1 pt-2 text-xs font-semibold uppercase tracking-wide text-muted">Recent</p>
              {recentPlants.map((p) => row(p, 'recent'))}
              <p className="px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-muted">All plants</p>
            </>
          )}

          {filtered.map((p) => row(p, 'all'))}

          {plants.isSuccess && filtered.length === 0 && (
            <p className="p-4 text-sm text-muted">No plants match your search.</p>
          )}
        </div>
      </div>
    </div>
  )
}
