"use client"

import { useMemo, useState } from "react"
import { AlertTriangle, Ban, Undo2 } from "lucide-react"

import { productionRunsApi } from "@/lib/api/production-runs"
import { errorMessage } from "@/lib/error-map"
import { ProductionRunGraph } from "@/types/production"
import { ItemListItem } from "@/types/master-data"
import { ReasonCode } from "@/types/stock"

import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogDescription, 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"

/**
 * Run-level actions (docs/21-FRONTEND-PHASE2.md §5): returning leftover raw material to stock,
 * and cancelling the run. Both are hidden once the run leaves InProgress — a completed run's
 * costs are closed (`409 RUN_COST_CLOSED`) and a completed run cannot be cancelled
 * (`409 RUN_NOT_CANCELLABLE`), so offering either would only produce an error.
 */

function fmt(n: number): string {
  return Number(n.toFixed(4)).toLocaleString(undefined, { maximumFractionDigits: 4 })
}

function money(n: number): string {
  return n.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 4 })
}

interface Returnable {
  runInputId: number
  stageName: string
  itemId: number
  /** Base UOM, matching what the return endpoint expects. */
  remaining: number
  consumedQty: number
  consumedValue: number
  returnedQty: number
  returnedValue: number
}

export function RunActions({
  run,
  items,
  reasonCodes,
  onActed,
}: {
  run: ProductionRunGraph
  items: ItemListItem[]
  reasonCodes: ReasonCode[]
  onActed: () => void
}) {
  const [dialog, setDialog] = useState<null | "leftover" | "cancel">(null)
  const [busy, setBusy] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const [selectedInputId, setSelectedInputId] = useState<number | null>(null)
  const [returnQty, setReturnQty] = useState("")
  const [returnReasonId, setReturnReasonId] = useState<number | null>(null)

  const [cancelReasonId, setCancelReasonId] = useState<number | null>(null)
  const [cancelNote, setCancelNote] = useState("")

  const itemName = useMemo(() => {
    const byId = new Map(items.map((i) => [i.itemId, i.name]))
    return (id: number) => byId.get(id) ?? `Item #${id}`
  }, [items])

  /**
   * Stock inputs with material still unreturned. Upstream inputs are excluded: they carry work
   * in progress that never entered stock, so there is nothing to return.
   */
  const returnable = useMemo<Returnable[]>(
    () =>
      run.stages.flatMap((stage) =>
        stage.inputs
          .filter((i) => i.source === "Stock" && i.itemId !== null && i.consumedQty - i.returnedQty > 0)
          .map((i) => ({
            runInputId: i.runInputId,
            stageName: stage.name,
            itemId: i.itemId!,
            remaining: i.consumedQty - i.returnedQty,
            consumedQty: i.consumedQty,
            consumedValue: i.consumedValue,
            returnedQty: i.returnedQty,
            returnedValue: i.returnedValue,
          }))
      ),
    [run.stages]
  )

  const selected = returnable.find((r) => r.runInputId === selectedInputId) ?? null
  const productionReasons = reasonCodes.filter((r) => r.context === "Production")

  // Weighted average cost of what this input actually consumed — the rate the returned stock
  // goes back in at, and the same figure the server derives from consumedValue/consumedQty.
  const weightedCost = selected && selected.consumedQty > 0 ? selected.consumedValue / selected.consumedQty : null

  function openLeftover() {
    setSelectedInputId(returnable[0]?.runInputId ?? null)
    setReturnQty("")
    setReturnReasonId(null)
    setError(null)
    setDialog("leftover")
  }

  function openCancel() {
    setCancelReasonId(null)
    setCancelNote("")
    setError(null)
    setDialog("cancel")
  }

  async function submit(action: () => Promise<unknown>) {
    setBusy(true)
    setError(null)
    try {
      await action()
      setDialog(null)
      onActed()
    } catch (err) {
      setError(errorMessage(err))
    } finally {
      setBusy(false)
    }
  }

  if (run.status !== "InProgress") return null

  const returnQtyNum = Number(returnQty)
  const returnValid =
    selected !== null &&
    returnReasonId !== null &&
    Number.isFinite(returnQtyNum) &&
    returnQtyNum > 0 &&
    returnQtyNum <= selected.remaining

  return (
    <>
      <div className="flex flex-wrap gap-2">
        <Button variant="outline" onClick={openLeftover} disabled={returnable.length === 0}>
          <Undo2 className="size-4" />
          Return leftover
        </Button>
        <Button variant="outline" className="text-destructive" onClick={openCancel}>
          <Ban className="size-4" />
          Cancel run
        </Button>
      </div>

      {/* ------------------------------------------------------------- leftover */}
      <Dialog open={dialog === "leftover"} onOpenChange={(open) => !open && setDialog(null)}>
        <DialogContent className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>Return leftover material</DialogTitle>
            <DialogDescription>
              Puts unused raw material back into stock at the cost it was consumed at, and takes it out of this
              run&apos;s cost pool.
            </DialogDescription>
          </DialogHeader>

          <div className="flex flex-col gap-4">
            <Field>
              <FieldLabel>Consumed material</FieldLabel>
              <Select<number> value={selectedInputId} onValueChange={(v) => { setSelectedInputId(v); setReturnQty("") }}>
                <SelectTrigger className="h-11! w-full text-base">
                  <SelectValue placeholder="Pick a material" />
                </SelectTrigger>
                <SelectContent>
                  {returnable.map((r) => (
                    <SelectItem key={r.runInputId} value={r.runInputId} className="text-base">
                      {itemName(r.itemId)} · {r.stageName} · {fmt(r.remaining)} left
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </Field>

            {selected && (
              <div className="flex flex-col gap-1 rounded-lg border border-border bg-muted/40 p-3 text-sm">
                <div className="flex justify-between">
                  <span className="text-muted-foreground">Consumed</span>
                  <span className="font-medium text-foreground">
                    {fmt(selected.consumedQty)} · {money(selected.consumedValue)}
                  </span>
                </div>
                <div className="flex justify-between">
                  <span className="text-muted-foreground">Already returned</span>
                  <span className="font-medium text-foreground">
                    {fmt(selected.returnedQty)} · {money(selected.returnedValue)}
                  </span>
                </div>
                <div className="flex justify-between">
                  <span className="text-muted-foreground">Weighted cost</span>
                  <span className="font-medium text-foreground">{weightedCost === null ? "—" : money(weightedCost)}</span>
                </div>
              </div>
            )}

            <Field>
              <FieldLabel htmlFor="return-qty">
                Quantity to return
                {selected && <span className="font-normal text-muted-foreground"> (base UOM, max {fmt(selected.remaining)})</span>}
              </FieldLabel>
              <Input
                id="return-qty"
                type="number"
                min={0}
                step="any"
                max={selected?.remaining}
                value={returnQty}
                onChange={(e) => setReturnQty(e.target.value)}
                placeholder={selected ? String(selected.remaining) : ""}
              />
            </Field>

            <Field>
              <FieldLabel>Reason</FieldLabel>
              <Select<number> value={returnReasonId} onValueChange={setReturnReasonId}>
                <SelectTrigger className="h-11! w-full text-base">
                  <SelectValue placeholder="Pick a reason" />
                </SelectTrigger>
                <SelectContent>
                  {productionReasons.map((r) => (
                    <SelectItem key={r.reasonCodeId} value={r.reasonCodeId} className="text-base">
                      {r.code} — {r.description}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </Field>

            {error && (
              <div className="flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive">
                <AlertTriangle className="mt-0.5 size-4 shrink-0" />
                <span>{error}</span>
              </div>
            )}
          </div>

          <div className="flex flex-col-reverse gap-3 pt-2 sm:flex-row sm:justify-end">
            <Button variant="outline" onClick={() => setDialog(null)} disabled={busy}>
              Cancel
            </Button>
            <Button
              disabled={busy || !returnValid}
              onClick={() =>
                submit(() =>
                  productionRunsApi.returnLeftover(run.runId, selected!.runInputId, {
                    qty: returnQtyNum,
                    reasonCodeId: returnReasonId!,
                  })
                )
              }
            >
              {busy ? "Returning…" : "Return to stock"}
            </Button>
          </div>
        </DialogContent>
      </Dialog>

      {/* --------------------------------------------------------------- cancel */}
      <Dialog open={dialog === "cancel"} onOpenChange={(open) => !open && setDialog(null)}>
        <DialogContent className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>Cancel this run?</DialogTitle>
            <DialogDescription>
              Everything consumed and not yet returned goes back into stock at its consumed cost. Scrapped output is
              written off — it never entered stock. This cannot be undone.
            </DialogDescription>
          </DialogHeader>

          <div className="flex flex-col gap-4">
            {returnable.length > 0 && (
              <div className="flex flex-col gap-1 rounded-lg border border-border bg-muted/40 p-3 text-sm">
                <p className="font-medium text-foreground">Will be returned to stock</p>
                {returnable.map((r) => (
                  <div key={r.runInputId} className="flex justify-between">
                    <span className="min-w-0 truncate text-muted-foreground">{itemName(r.itemId)}</span>
                    <span className="shrink-0 font-medium text-foreground">{fmt(r.remaining)}</span>
                  </div>
                ))}
              </div>
            )}

            <Field>
              <FieldLabel>Reason</FieldLabel>
              <Select<number> value={cancelReasonId} onValueChange={setCancelReasonId}>
                <SelectTrigger className="h-11! w-full text-base">
                  <SelectValue placeholder="Pick a reason" />
                </SelectTrigger>
                <SelectContent>
                  {productionReasons.map((r) => (
                    <SelectItem key={r.reasonCodeId} value={r.reasonCodeId} className="text-base">
                      {r.code} — {r.description}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </Field>

            <Field>
              <FieldLabel htmlFor="cancel-note">Note (optional)</FieldLabel>
              <Input id="cancel-note" value={cancelNote} onChange={(e) => setCancelNote(e.target.value)} />
            </Field>

            {error && (
              <div className="flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive">
                <AlertTriangle className="mt-0.5 size-4 shrink-0" />
                <span>{error}</span>
              </div>
            )}
          </div>

          <div className="flex flex-col-reverse gap-3 pt-2 sm:flex-row sm:justify-end">
            <Button variant="outline" onClick={() => setDialog(null)} disabled={busy}>
              Keep run
            </Button>
            <Button
              variant="destructive"
              disabled={busy || cancelReasonId === null}
              onClick={() =>
                submit(() =>
                  productionRunsApi.cancel(run.runId, {
                    reasonCodeId: cancelReasonId!,
                    note: cancelNote.trim() || null,
                  })
                )
              }
            >
              {busy ? "Cancelling…" : "Cancel run"}
            </Button>
          </div>
        </DialogContent>
      </Dialog>
    </>
  )
}
