import { useState, type FormEvent } from 'react'
import { Alert } from '@/components/ui/Alert'
import { Button } from '@/components/ui/Button'
import { Dialog } from '@/components/ui/Dialog'
import { Field, SelectField, TextAreaField, TextField } from '@/components/ui/Field'
import { errorMessage } from '@/lib/api'
import {
  CATEGORY_LABELS,
  PLANT_CATEGORIES,
  conflictPlant,
  useCreatePlant,
  useUpdatePlant,
  type Plant,
  type PlantCategory,
  type PlantInput,
} from '@/lib/plants'
import { safeExternalUrl } from '@/lib/seedLots'
import { editSpacingField, spacingField, spacingUnitLabel, type LengthField, type UnitPref } from '@/lib/units'
import { ColorSwatches, CURATED_SWATCHES, expandHex } from './ColorSwatches'

// Markers are monograms now, but the API still carries an icon per plant; a
// plant made here gets the neutral sprout so nothing downstream sees an empty one.
const DEFAULT_ICON = '🌱'
const categoryOptions = PLANT_CATEGORIES.map((c) => ({ value: c, label: CATEGORY_LABELS[c] }))

/**
 * "A new plant" — or edit (`plant`), or a fresh create prefilled from another
 * (`template`, the Duplicate action; the only way to customize a built-in).
 * Spacing is typed in the page's unit and stored in centimeters — as a
 * LengthField, so a Save that didn't touch it sends the centimeters that were
 * loaded rather than re-parsing "17.7 in" into 44.958. A 409 rebases onto the
 * server's current row.
 */
export function PlantDialog({
  plant,
  template,
  unit,
  onClose,
}: {
  plant?: Plant
  template?: Plant
  unit: UnitPref
  onClose: () => void
}) {
  const isEdit = !!plant
  const source = plant ?? template
  const create = useCreatePlant()
  const update = useUpdatePlant()
  const pending = create.isPending || update.isPending

  const [name, setName] = useState(source ? (isEdit ? source.name : `${source.name} (copy)`) : '')
  const [category, setCategory] = useState<PlantCategory>(source?.category ?? 'vegetable')
  const [spacing, setSpacing] = useState<LengthField>(() => spacingField(source?.spacingCm ?? 30, unit))
  const [color, setColor] = useState(expandHex(source?.color ?? CURATED_SWATCHES[0]))
  const [days, setDays] = useState(source?.daysToMaturity != null ? String(source.daysToMaturity) : '')
  const [vendor, setVendor] = useState(source?.vendor ?? '')
  const [sourceUrl, setSourceUrl] = useState(source?.sourceUrl ?? '')
  const [notes, setNotes] = useState(source?.notes ?? '')
  const [more, setMore] = useState(!!(source?.vendor || source?.sourceUrl || source?.notes))
  const [version, setVersion] = useState(plant?.version ?? 0)
  const [conflict, setConflict] = useState<string | null>(null)
  const [formError, setFormError] = useState<string | null>(null)
  const unitLabel = spacingUnitLabel(unit)

  async function onSubmit(e: FormEvent) {
    e.preventDefault()
    setFormError(null)
    setConflict(null)
    if (!name.trim()) {
      setFormError('Give the plant a name.')
      return
    }
    const spacingCm = spacing.cm
    if (spacingCm === null || spacingCm < 1) {
      setFormError(`Spacing must be at least 1 ${unitLabel}.`)
      return
    }
    let daysToMaturity: number | null = null
    if (days.trim()) {
      const d = Number(days)
      if (!Number.isInteger(d) || d < 1) {
        setFormError('Days to maturity is a whole number of days, or blank.')
        return
      }
      daysToMaturity = d
    }
    if (sourceUrl.trim() && !safeExternalUrl(sourceUrl.trim())) {
      setFormError('The source link needs to be a full http:// or https:// address.')
      return
    }
    const input: PlantInput = {
      name: name.trim(),
      category,
      spacingCm,
      color,
      icon: source?.icon || DEFAULT_ICON,
      daysToMaturity,
      sourceUrl: sourceUrl.trim(),
      vendor: vendor.trim(),
      notes: notes.trim(),
    }
    // Nothing changed: close without a request, so a look-and-Save doesn't bump
    // the version for every garden that shares the plant.
    if (isEdit && (Object.keys(input) as (keyof PlantInput)[]).every((k) => input[k] === (k === 'color' ? expandHex(plant.color) : plant[k]))) {
      onClose()
      return
    }
    try {
      if (isEdit) await update.mutateAsync({ id: plant.id, ...input, version })
      else await create.mutateAsync(input)
      onClose()
    } catch (err) {
      const current = conflictPlant(err)
      if (current) {
        setVersion(current.version)
        setName(current.name)
        setCategory(current.category)
        setSpacing(spacingField(current.spacingCm, unit))
        setColor(expandHex(current.color))
        setDays(current.daysToMaturity != null ? String(current.daysToMaturity) : '')
        setVendor(current.vendor)
        setSourceUrl(current.sourceUrl)
        setNotes(current.notes)
        setConflict('This plant changed elsewhere. The latest values are shown — look them over and save again.')
        return
      }
      setFormError(errorMessage(err, isEdit ? 'Could not save the plant.' : 'Could not add the plant.'))
    }
  }

  return (
    <Dialog title={isEdit ? `Edit ${plant.name}` : 'A new plant'} onClose={onClose} busy={pending}>
      <form onSubmit={onSubmit} className="flex flex-col gap-3.5">
        {conflict && <Alert tone="info">{conflict}</Alert>}
        <TextField label="Name" name="name" required autoFocus placeholder="Delicata squash" value={name} onChange={(e) => setName(e.target.value)} />
        <div className="flex gap-2.5">
          <SelectField
            label="Category"
            name="category"
            value={category}
            onChange={(e) => setCategory(e.target.value as PlantCategory)}
            options={categoryOptions}
            wrapperClassName="flex-1"
          />
          <TextField
            label={`Spacing (${unitLabel})`}
            name="spacing"
            type="number"
            inputMode="decimal"
            step="any"
            min="1"
            required
            value={spacing.text}
            onChange={(e) => setSpacing(editSpacingField(e.target.value, unit))}
            wrapperClassName="flex-1"
          />
        </div>
        <Field label="Marker color">
          <ColorSwatches value={color} onChange={setColor} />
        </Field>
        <TextField
          label="Days to maturity (optional)"
          name="days"
          type="number"
          inputMode="numeric"
          step="1"
          min="1"
          value={days}
          onChange={(e) => setDays(e.target.value)}
        />

        {!more ? (
          <button type="button" className="btn btn-ghost self-start text-[13px]" onClick={() => setMore(true)}>
            Vendor, source link &amp; notes…
          </button>
        ) : (
          <div className="flex flex-col gap-3.5 rounded-md border border-divider bg-bg p-3.5">
            <div className="flex gap-2.5">
              <TextField label="Vendor" name="vendor" placeholder="Johnny's" value={vendor} onChange={(e) => setVendor(e.target.value)} wrapperClassName="flex-1" />
              <TextField
                label="Source link"
                name="sourceUrl"
                type="url"
                inputMode="url"
                placeholder="https://…"
                value={sourceUrl}
                onChange={(e) => setSourceUrl(e.target.value)}
                wrapperClassName="flex-1"
              />
            </div>
            <TextAreaField label="Notes" name="notes" rows={2} value={notes} onChange={(e) => setNotes(e.target.value)} />
          </div>
        )}

        {formError && <Alert>{formError}</Alert>}
        <div className="flex justify-end gap-2">
          <Button type="button" onClick={onClose} disabled={pending}>
            Never mind
          </Button>
          <Button type="submit" variant="primary" disabled={pending}>
            {pending ? 'Saving…' : isEdit ? 'Save' : 'Add it'}
          </Button>
        </div>
      </form>
    </Dialog>
  )
}
