"use client"

import type { ReactNode } from "react"
import { Plus, Trash2 } from "lucide-react"

import { cn } from "@/lib/utils"
import { baseUomLabel, contentUnitLabel } from "@/lib/uom-label"
import { CustomFieldType, StageInputSource, StageQtyUnit } from "@/types/production"
import { ItemListItem, Uom } from "@/types/master-data"
import {
  BuilderFieldDef,
  BuilderInput,
  BuilderOutput,
  StageNodeData,
  newKey,
  newLocalId,
} from "./types"

import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Field, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Switch } from "@/components/ui/switch"

function slugify(label: string) {
  return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")
}

const ROLE_SUGGESTIONS = ["Assembly", "QA", "Welding", "Packing", "Inspection", "Cutting", "Soldering"]
const FIELD_TYPES: CustomFieldType[] = ["Text", "Number", "Checkbox", "Date", "Select"]

export interface UpstreamOutputOption {
  stageKey: string
  stageName: string
  outputKey: string
  outputName: string
}

/**
 * One quantity row: the number, plus whatever names its unit.
 *
 * The unit is no longer a free choice. An input's is decided by its item (and, when that item
 * has a content size, by the Pack/Content toggle); an output's comes from its item, or from a
 * WIP label when it has none. So each caller supplies its own `unit` control and this row only
 * owns the number.
 */
function QtyRow({
  qty,
  readOnly,
  onQtyChange,
  unit,
}: {
  qty: number
  readOnly: boolean
  onQtyChange: (qty: number) => void
  unit: ReactNode
}) {
  return (
    <div className="flex items-center gap-2">
      <Input
        type="number"
        min={0}
        step="any"
        value={qty}
        disabled={readOnly}
        onChange={(e) => onQtyChange(Number(e.target.value) || 0)}
        className="h-8 text-sm"
        placeholder="Qty per batch"
        aria-label="Quantity per batch"
      />
      {unit}
    </div>
  )
}

/** Static unit name, for the rows whose unit is derived and therefore not editable. */
function UnitLabel({ children }: { children: ReactNode }) {
  return (
    <span className="flex h-8 w-24 shrink-0 items-center text-sm text-muted-foreground">{children}</span>
  )
}

/**
 * Names the unit of a stage input's quantity.
 *
 * Only one case is a choice: a Stock input whose item declares a content size can be written
 * either as an amount of that content (2000 ml) or as a pack count (4 bottles). Everything
 * else has exactly one possible unit, so it renders as a label rather than a control.
 */
function InputUnitControl({
  input,
  items,
  uoms,
  readOnly,
  onChange,
}: {
  input: BuilderInput
  items: ItemListItem[]
  uoms: Uom[]
  readOnly: boolean
  onChange: (unit: StageQtyUnit) => void
}) {
  if (input.source === "Upstream") return <UnitLabel>WIP</UnitLabel>

  const item = items.find((candidate) => candidate.itemId === input.itemId)
  const packName = baseUomLabel(items, uoms, input.itemId)
  const contentName = contentUnitLabel(items, input.itemId)

  if (!item?.contentBaseQty || !contentName) return <UnitLabel>{packName}</UnitLabel>

  return (
    <Select<string> value={input.qtyUnit} onValueChange={(v) => v && onChange(v as StageQtyUnit)}>
      <SelectTrigger className="h-8! w-24 shrink-0 text-sm" disabled={readOnly} aria-label="Quantity unit">
        <SelectValue />
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="Content" className="text-sm">{contentName}</SelectItem>
        <SelectItem value="Pack" className="text-sm">{packName}</SelectItem>
      </SelectContent>
    </Select>
  )
}

export function StageEditorPanel({
  data,
  isTerminal,
  upstreamOptions,
  items,
  uoms,
  readOnly,
  onChange,
  onDelete,
  onClose,
}: {
  data: StageNodeData
  isTerminal: boolean
  upstreamOptions: UpstreamOutputOption[]
  items: ItemListItem[]
  uoms: Uom[]
  readOnly: boolean
  onChange: (patch: Partial<StageNodeData>) => void
  onDelete: () => void
  onClose: () => void
}) {
  function updateInput(localId: string, patch: Partial<BuilderInput>) {
    onChange({ inputs: data.inputs.map((i) => (i.localId === localId ? { ...i, ...patch } : i)) })
  }
  function addInput() {
    onChange({
      inputs: [
        ...data.inputs,
        { localId: newLocalId(), source: "Stock", itemId: null, fromOutputKey: null, qtyUnit: "Pack", qtyPerBatch: 1 },
      ],
    })
  }
  function removeInput(localId: string) {
    onChange({ inputs: data.inputs.filter((i) => i.localId !== localId) })
  }

  /**
   * Switching source clears the other side's field. Leaving a stale `itemId` on an Upstream
   * input (or a stale `fromOutputKey` on a Stock one) is a 422 GRAPH_INPUT_SOURCE_INVALID —
   * the validator rejects an input that carries both.
   */
  function changeInputSource(localId: string, source: StageInputSource) {
    updateInput(
      localId,
      // WIP has no content size, so an Upstream input can only ever be counted in whole units.
      source === "Stock" ? { source, fromOutputKey: null } : { source, itemId: null, qtyUnit: "Pack" },
    )
  }

  /**
   * Default to content units when the item has a content size — a recipe is far more often
   * written as "2000 ml of syrup" than "4 bottles" — and to packs otherwise. Still switchable.
   */
  function pickInputItem(input: BuilderInput, itemId: number) {
    const item = items.find((i) => i.itemId === itemId)
    updateInput(input.localId, { itemId, qtyUnit: item?.contentBaseQty ? "Content" : "Pack" })
  }

  function updateOutput(key: string, patch: Partial<BuilderOutput>) {
    onChange({ outputs: data.outputs.map((o) => (o.key === key ? { ...o, ...patch } : o)) })
  }
  function addOutput() {
    onChange({
      outputs: [...data.outputs, { key: newKey(), itemId: null, name: "", uomId: null, qtyPerBatch: 1 }],
    })
  }
  function removeOutput(key: string) {
    onChange({ outputs: data.outputs.filter((o) => o.key !== key) })
  }

  /** The terminal output's name mirrors the finished item, so the two can't drift apart. */
  function pickOutputItem(output: BuilderOutput, itemId: number) {
    const item = items.find((i) => i.itemId === itemId)
    updateOutput(output.key, {
      itemId,
      name: item?.name ?? output.name,
      uomId: output.uomId ?? item?.baseUomId ?? null,
    })
  }

  function updateField(localId: string, patch: Partial<BuilderFieldDef>) {
    onChange({
      fieldDefs: data.fieldDefs.map((f) => {
        if (f.localId !== localId) return f
        const next = { ...f, ...patch }
        if (patch.label !== undefined) next.key = slugify(patch.label) || f.key
        return next
      }),
    })
  }
  function addField() {
    onChange({
      fieldDefs: [
        ...data.fieldDefs,
        { localId: newLocalId(), key: "", label: "", type: "Text", options: [], required: false },
      ],
    })
  }
  function removeField(localId: string) {
    onChange({ fieldDefs: data.fieldDefs.filter((f) => f.localId !== localId) })
  }

  return (
    <Dialog open onOpenChange={(next) => !next && onClose()}>
      <DialogContent className="max-h-[85vh] w-full max-w-lg overflow-y-auto sm:max-w-lg">
        <DialogHeader>
          <DialogTitle>Stage editor</DialogTitle>
        </DialogHeader>

        <div className="flex flex-col gap-4">
          <Field>
            <FieldLabel>Name</FieldLabel>
            <Input value={data.name} disabled={readOnly} onChange={(e) => onChange({ name: e.target.value })} placeholder="e.g. Welding" />
          </Field>

          <Field>
            <FieldLabel>Role label</FieldLabel>
            <Input
              value={data.roleLabel}
              disabled={readOnly}
              onChange={(e) => onChange({ roleLabel: e.target.value })}
              placeholder="e.g. QA"
              list="role-suggestions"
            />
            <datalist id="role-suggestions">
              {ROLE_SUGGESTIONS.map((r) => (
                <option key={r} value={r} />
              ))}
            </datalist>
          </Field>

          <Field>
            <FieldLabel>Estimated minutes</FieldLabel>
            <Input
              type="number"
              min={0}
              value={data.estimatedMinutes}
              disabled={readOnly}
              onChange={(e) => onChange({ estimatedMinutes: Number(e.target.value) || 0 })}
            />
          </Field>

          {/* Inputs */}
          <div>
            <div className="mb-2 flex items-center justify-between">
              <p className="text-sm font-semibold text-foreground">Inputs</p>
              {!readOnly && (
                <Button type="button" variant="ghost" size="sm" onClick={addInput}>
                  <Plus className="size-4" />
                  Add
                </Button>
              )}
            </div>
            <div className="flex flex-col gap-2">
              {data.inputs.length === 0 && <p className="text-sm text-muted-foreground">No inputs yet.</p>}
              {data.inputs.map((input) => (
                <div key={input.localId} className="rounded-lg border border-border p-2.5">
                  <div className="mb-2 flex items-center justify-between gap-2">
                    <Select<StageInputSource>
                      value={input.source}
                      onValueChange={(v) => v && changeInputSource(input.localId, v)}
                    >
                      <SelectTrigger className="h-8! flex-1 text-sm" disabled={readOnly}>
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="Stock" className="text-sm">Stock</SelectItem>
                        <SelectItem value="Upstream" className="text-sm">Upstream</SelectItem>
                      </SelectContent>
                    </Select>
                    {!readOnly && (
                      <button type="button" onClick={() => removeInput(input.localId)} className="shrink-0 text-muted-foreground hover:text-destructive" aria-label="Remove input">
                        <Trash2 className="size-4" />
                      </button>
                    )}
                  </div>

                  <div className="flex flex-col gap-2">
                    {input.source === "Stock" ? (
                      <Select<number>
                        value={input.itemId}
                        onValueChange={(v) => v && pickInputItem(input, v)}
                      >
                        <SelectTrigger className="h-8! w-full text-sm" disabled={readOnly}>
                          <SelectValue placeholder="Pick an item" />
                        </SelectTrigger>
                        <SelectContent>
                          {items.map((i) => (
                            <SelectItem key={i.itemId} value={i.itemId} className="text-sm">
                              {i.name} · {i.sku}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    ) : (
                      <Select<string>
                        value={input.fromOutputKey}
                        onValueChange={(v) => v && updateInput(input.localId, { fromOutputKey: v })}
                      >
                        <SelectTrigger className="h-8! w-full text-sm" disabled={readOnly || upstreamOptions.length === 0}>
                          <SelectValue placeholder={upstreamOptions.length === 0 ? "No upstream stages connected" : "Pick an upstream output"} />
                        </SelectTrigger>
                        <SelectContent>
                          {upstreamOptions.map((o) => (
                            <SelectItem key={o.outputKey} value={o.outputKey} className="text-sm">
                              {o.stageName} — {o.outputName}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    )}

                    <QtyRow
                      qty={input.qtyPerBatch}
                      readOnly={readOnly}
                      onQtyChange={(qtyPerBatch) => updateInput(input.localId, { qtyPerBatch })}
                      unit={<InputUnitControl
                        input={input}
                        items={items}
                        uoms={uoms}
                        readOnly={readOnly}
                        onChange={(qtyUnit) => updateInput(input.localId, { qtyUnit })}
                      />}
                    />
                  </div>
                </div>
              ))}
            </div>
          </div>

          {/* Outputs */}
          <div>
            <div className="mb-2 flex items-center justify-between">
              <p className="text-sm font-semibold text-foreground">
                Outputs{isTerminal && <span className="ml-1.5 font-normal text-muted-foreground">(terminal — finished good)</span>}
              </p>
              {!readOnly && (
                <Button type="button" variant="ghost" size="sm" onClick={addOutput}>
                  <Plus className="size-4" />
                  Add
                </Button>
              )}
            </div>
            <div className="flex flex-col gap-2">
              {data.outputs.length === 0 && <p className="text-sm text-muted-foreground">No outputs yet.</p>}
              {data.outputs.map((output) => (
                <div key={output.key} className="rounded-lg border border-border p-2.5">
                  <div className="mb-2 flex items-center gap-2">
                    {isTerminal ? (
                      <Select<number> value={output.itemId} onValueChange={(v) => v && pickOutputItem(output, v)}>
                        <SelectTrigger className="h-8! flex-1 text-sm" disabled={readOnly}>
                          <SelectValue placeholder="Pick the finished-good item" />
                        </SelectTrigger>
                        <SelectContent>
                          {items.map((i) => (
                            <SelectItem key={i.itemId} value={i.itemId} className="text-sm">
                              {i.name} · {i.sku}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    ) : (
                      <Input
                        value={output.name}
                        disabled={readOnly}
                        onChange={(e) => updateOutput(output.key, { name: e.target.value })}
                        placeholder="Output name (work in progress)"
                        className="h-8 flex-1 text-sm"
                      />
                    )}
                    {!readOnly && (
                      <button type="button" onClick={() => removeOutput(output.key)} className="shrink-0 text-muted-foreground hover:text-destructive" aria-label="Remove output">
                        <Trash2 className="size-4" />
                      </button>
                    )}
                  </div>
                  <QtyRow
                    qty={output.qtyPerBatch}
                    readOnly={readOnly}
                    onQtyChange={(qtyPerBatch) => updateOutput(output.key, { qtyPerBatch })}
                    unit={
                      // A finished good is counted in its item's own unit; only WIP names one.
                      output.itemId !== null ? (
                        <UnitLabel>{baseUomLabel(items, uoms, output.itemId)}</UnitLabel>
                      ) : (
                        <Select<number> value={output.uomId} onValueChange={(v) => v && updateOutput(output.key, { uomId: v })}>
                          <SelectTrigger className="h-8! w-24 shrink-0 text-sm" disabled={readOnly} aria-label="WIP unit">
                            <SelectValue placeholder="Unit" />
                          </SelectTrigger>
                          <SelectContent>
                            {uoms.map((u) => (
                              <SelectItem key={u.uomId} value={u.uomId} className="text-sm">
                                {u.name}
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      )
                    }
                  />
                </div>
              ))}
            </div>
          </div>

          {/* Custom fields */}
          <div>
            <div className="mb-2 flex items-center justify-between">
              <p className="text-sm font-semibold text-foreground">Custom fields</p>
              {!readOnly && (
                <Button type="button" variant="ghost" size="sm" onClick={addField}>
                  <Plus className="size-4" />
                  Add
                </Button>
              )}
            </div>
            <div className="flex flex-col gap-2">
              {data.fieldDefs.length === 0 && <p className="text-sm text-muted-foreground">No custom fields.</p>}
              {data.fieldDefs.map((field) => (
                <div key={field.localId} className="rounded-lg border border-border p-2.5">
                  <div className="mb-2 flex items-center gap-2">
                    <Input
                      value={field.label}
                      disabled={readOnly}
                      onChange={(e) => updateField(field.localId, { label: e.target.value })}
                      placeholder="Label"
                      className="h-8 flex-1 text-sm"
                    />
                    {!readOnly && (
                      <button type="button" onClick={() => removeField(field.localId)} className="shrink-0 text-muted-foreground hover:text-destructive" aria-label="Remove field">
                        <Trash2 className="size-4" />
                      </button>
                    )}
                  </div>
                  {field.key && <p className="mb-2 font-mono text-xs text-muted-foreground">key: {field.key}</p>}
                  <div className="flex items-center gap-2">
                    <Select<CustomFieldType>
                      value={field.type}
                      onValueChange={(v) => v && updateField(field.localId, { type: v })}
                    >
                      <SelectTrigger className="h-8! flex-1 text-sm" disabled={readOnly}>
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        {FIELD_TYPES.map((t) => (
                          <SelectItem key={t} value={t} className="text-sm">{t}</SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                    <div className="flex items-center gap-1.5">
                      <Switch
                        size="sm"
                        checked={field.required}
                        disabled={readOnly}
                        onCheckedChange={(checked) => updateField(field.localId, { required: checked })}
                      />
                      <span className="text-xs text-muted-foreground">Required</span>
                    </div>
                  </div>
                  {field.type === "Select" && (
                    <Input
                      value={field.options.join(", ")}
                      disabled={readOnly}
                      onChange={(e) => updateField(field.localId, { options: e.target.value.split(",").map((s) => s.trim()).filter(Boolean) })}
                      placeholder="Options, comma separated"
                      className="mt-2 h-8 text-sm"
                    />
                  )}
                </div>
              ))}
            </div>
          </div>

          {!readOnly && (
            <Button type="button" variant="outline" className={cn("mt-2 text-destructive hover:bg-destructive/10")} onClick={onDelete}>
              <Trash2 className="size-4" />
              Delete stage
            </Button>
          )}
        </div>
      </DialogContent>
    </Dialog>
  )
}
