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 { TextAreaField, TextField } from '@/components/ui/Field'
import { Seg } from '@/components/ui/Seg'
import { Toggle } from '@/components/ui/Toggle'
import { errorMessage } from '@/lib/api'
import { conflictGarden, useCreateGarden, useUpdateGarden, type Garden } from '@/lib/gardens'
import {
  cmFromFtIn,
  convertDimensionField,
  dimensionField,
  dimensionInputMode,
  dimensionUnitLabel,
  editDimensionField,
  formatCm,
  isValidDimensionCm,
  MIN_GARDEN_GRID_CM,
  type LengthField,
  type UnitPref,
} from '@/lib/units'

// A new plot: the design's 20′ × 12′, spoken in feet; the garden grid defaults
// to a foot (the server's 1 m for a metric garden).
const DEFAULT_W_FT = 20
const DEFAULT_H_FT = 12
const DEFAULT_GRID_CM = 100

const unitOptions = [
  { value: 'imperial' as const, label: 'ft' },
  { value: 'metric' as const, label: 'm' },
]

function entryHint(unit: UnitPref): string {
  return unit === 'imperial' ? `Sizes read as feet and inches — 8' 6", 8', or 8.5 for feet.` : 'Sizes are in meters, e.g. 2.5.'
}

/**
 * "A new garden" (no garden) or edit (garden given). Dimensions are typed in the
 * chosen unit and stored as centimeters: each field is a LengthField, so
 * switching units re-shows the same centimeters and a Save sends exactly what
 * was loaded unless the person typed over it (re-parsing the display string is
 * how 900 cm once became 899.922). A 409 rebases the form onto the server's
 * fresh row.
 */
export function GardenDialog({ garden, onClose }: { garden?: Garden; onClose: () => void }) {
  const isEdit = !!garden
  const create = useCreateGarden()
  const update = useUpdateGarden()
  const pending = create.isPending || update.isPending

  const initialUnit: UnitPref = garden?.unitPref ?? 'imperial'
  const [name, setName] = useState(garden?.name ?? '')
  const [unit, setUnit] = useState<UnitPref>(initialUnit)
  const [width, setWidth] = useState<LengthField>(() => dimensionField(garden?.widthCm ?? cmFromFtIn(DEFAULT_W_FT), initialUnit))
  const [height, setHeight] = useState<LengthField>(() => dimensionField(garden?.heightCm ?? cmFromFtIn(DEFAULT_H_FT), initialUnit))
  const [gridSize, setGridSize] = useState<LengthField>(() =>
    dimensionField(garden?.gridSizeCm ?? (initialUnit === 'imperial' ? cmFromFtIn(1) : DEFAULT_GRID_CM), initialUnit),
  )
  const [snapToGrid, setSnapToGrid] = useState(garden?.snapToGrid ?? false)
  const [notes, setNotes] = useState(garden?.notes ?? '')
  const [more, setMore] = useState(isEdit && (!!garden.notes || garden.snapToGrid))
  const [version, setVersion] = useState(garden?.version ?? 0)
  const [conflict, setConflict] = useState<string | null>(null)
  const [formError, setFormError] = useState<string | null>(null)

  function changeUnit(next: UnitPref) {
    setWidth((f) => convertDimensionField(f, next))
    setHeight((f) => convertDimensionField(f, next))
    setGridSize((f) => convertDimensionField(f, next))
    setUnit(next)
  }

  const gridSizeCm = gridSize.cm
  const gridTooFine = gridSizeCm !== null && gridSizeCm > 0 && gridSizeCm < MIN_GARDEN_GRID_CM

  async function onSubmit(e: FormEvent) {
    e.preventDefault()
    setFormError(null)
    setConflict(null)
    if (!name.trim()) {
      setFormError('Give the garden a name.')
      return
    }
    const widthCm = width.cm
    const heightCm = height.cm
    if (widthCm === null || heightCm === null) {
      setFormError(entryHint(unit))
      return
    }
    if (!isValidDimensionCm(widthCm) || !isValidDimensionCm(heightCm)) {
      setFormError('Width and depth must be between 1 cm and 100 m.')
      return
    }
    if (gridSizeCm === null || !isValidDimensionCm(gridSizeCm)) {
      setFormError('The grid must be between 1 cm and 100 m.')
      return
    }
    const input = { name: name.trim(), widthCm, heightCm, unitPref: unit, notes: notes.trim(), gridSizeCm, snapToGrid }
    // Nothing changed: close without a request. A PATCH that writes the same
    // row still bumps the version and lands an "Edited garden settings" step
    // in History that undoes nothing.
    if (isEdit && (Object.keys(input) as (keyof typeof input)[]).every((k) => input[k] === garden[k])) {
      onClose()
      return
    }
    try {
      if (isEdit) await update.mutateAsync({ id: garden.id, ...input, version })
      else await create.mutateAsync(input)
      onClose()
    } catch (err) {
      const current = conflictGarden(err)
      if (current) {
        setVersion(current.version)
        setName(current.name)
        setUnit(current.unitPref)
        setWidth(dimensionField(current.widthCm, current.unitPref))
        setHeight(dimensionField(current.heightCm, current.unitPref))
        setGridSize(dimensionField(current.gridSizeCm, current.unitPref))
        setSnapToGrid(current.snapToGrid)
        setNotes(current.notes)
        setConflict('This garden changed elsewhere. The latest values are shown — look them over and save again.')
        return
      }
      setFormError(errorMessage(err, isEdit ? 'Could not save the garden.' : 'Could not create the garden.'))
    }
  }

  const u = dimensionUnitLabel(unit)
  const inputMode = dimensionInputMode(unit)

  return (
    <Dialog title={isEdit ? `Edit ${garden.name}` : 'A new garden'} 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="Back forty" value={name} onChange={(e) => setName(e.target.value)} />
        <div className="flex gap-2.5">
          <TextField
            label={`Width (${u})`}
            name="width"
            type="text"
            inputMode={inputMode}
            required
            value={width.text}
            onChange={(e) => setWidth(editDimensionField(e.target.value, unit))}
            wrapperClassName="flex-1"
          />
          <TextField
            label={`Depth (${u})`}
            name="height"
            type="text"
            inputMode={inputMode}
            required
            value={height.text}
            onChange={(e) => setHeight(editDimensionField(e.target.value, unit))}
            wrapperClassName="flex-1"
          />
          <div className="field">
            <label>Units</label>
            <Seg options={unitOptions} value={unit} onChange={changeUnit} label="Units" />
          </div>
        </div>
        <div className="text-xs text-ink-mute">Stored in centimeters under the hood — {unit === 'imperial' ? 'feet are' : 'meters are'} just how you talk.</div>

        {!more ? (
          <button type="button" className="btn btn-ghost self-start text-[13px]" onClick={() => setMore(true)}>
            Grid &amp; notes…
          </button>
        ) : (
          <div className="flex flex-col gap-3.5 rounded-md border border-divider bg-bg p-3.5">
            <div className="flex items-end gap-2.5">
              <TextField
                label={`Garden grid (${u})`}
                name="gridSize"
                type="text"
                inputMode={inputMode}
                value={gridSize.text}
                onChange={(e) => setGridSize(editDimensionField(e.target.value, unit))}
                wrapperClassName="flex-1"
                hint={
                  gridTooFine && gridSizeCm !== null
                    ? `${formatCm(gridSizeCm, unit)} is very fine for a whole-garden grid — plant spacing lives on each bed.`
                    : undefined
                }
              />
              <div className="field">
                <label>Snap objects</label>
                <Toggle on={snapToGrid} onChange={setSnapToGrid} label="Snap objects to the garden grid" />
              </div>
            </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' : 'Break ground'}
          </Button>
        </div>
      </form>
    </Dialog>
  )
}
