"use client"

import { useEffect, useState } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
import { ExternalLink, Plus, RefreshCw, Trash2 } from "lucide-react"

import { grnsApi } from "@/lib/api/grns"
import { purchaseOrdersApi } from "@/lib/api/purchase-orders"
import { vendorsApi } from "@/lib/api/vendors"
import { warehousesApi } from "@/lib/api/warehouses"
import { itemsApi } from "@/lib/api/items"
import { uomsApi } from "@/lib/api/uoms"
import { baseUomLabel } from "@/lib/uom-label"
import { errorMessage } from "@/lib/error-map"
import { validateLine, splitSerials, grnHeaderSchema } from "@/lib/validations/grn"
import { cn } from "@/lib/utils"
import { CreateGrnLineInput, HoldStatus } from "@/types/grn"
import { PurchaseOrder, PurchaseOrderSummary } from "@/types/procurement"
import { Bin, ItemListItem, Uom, Vendor, Warehouse } from "@/types/master-data"

import { Button, buttonVariants } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { FieldError } from "@/components/ui/field"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Skeleton } from "@/components/ui/skeleton"
import { toast } from "@/components/ui/toast"

type Mode = "po" | "direct"

interface DraftLine {
  key: string
  poLineId: number | null
  itemId: number | null
  binId: number | null
  qty: string
  unitCost: string
  /** PO line price when prefilled from a PO; drives the variance hint. */
  poUnitPrice: number | null
  discountPct: string
  vatPct: string
  holdStatus: HoldStatus
  batchNo: string
  expiryDate: string
  serialNumbersText: string
}

/** Mirror of the server's line arithmetic — display only (docs/20 §3, server stays authoritative). */
function computeLine(l: DraftLine) {
  const qty = Number(l.qty) || 0
  const gross = Number(l.unitCost) || 0
  const disc = Number(l.discountPct) || 0
  const vat = Number(l.vatPct) || 0
  const netUnitCost = gross * (1 - disc / 100)
  const receivedValue = qty * netUnitCost
  const vatAmount = receivedValue * (vat / 100)
  return { netUnitCost, receivedValue, vatAmount, lineTotal: receivedValue + vatAmount }
}

let keySeq = 0
function newKey() {
  keySeq += 1
  return `gline-${keySeq}`
}

function emptyLine(): DraftLine {
  return {
    key: newKey(),
    poLineId: null,
    itemId: null,
    binId: null,
    qty: "",
    unitCost: "",
    poUnitPrice: null,
    discountPct: "0",
    vatPct: "0",
    holdStatus: "Available",
    batchNo: "",
    expiryDate: "",
    serialNumbersText: "",
  }
}

export default function NewGrnPage() {
  const router = useRouter()

  const [mode, setMode] = useState<Mode>("po")

  const [warehouses, setWarehouses] = useState<Warehouse[] | null>(null)
  const [items, setItems] = useState<ItemListItem[] | null>(null)
  const [uoms, setUoms] = useState<Uom[] | null>(null)
  const [vendors, setVendors] = useState<Vendor[] | null>(null)
  const [purchaseOrders, setPurchaseOrders] = useState<PurchaseOrderSummary[] | null>(null)
  const [bins, setBins] = useState<Bin[]>([])
  const [loadError, setLoadError] = useState<string | null>(null)

  const [warehouseId, setWarehouseId] = useState<number | null>(null)
  const [vendorId, setVendorId] = useState<number | null>(null)
  const [poId, setPoId] = useState<number | null>(null)
  const [poLoading, setPoLoading] = useState(false)
  const [lines, setLines] = useState<DraftLine[]>([emptyLine()])

  const [headerError, setHeaderError] = useState<string | null>(null)
  const [lineErrors, setLineErrors] = useState<Record<string, Record<string, string>>>({})
  const [submitError, setSubmitError] = useState<string | null>(null)
  const [submitting, setSubmitting] = useState(false)
  const [refreshingItems, setRefreshingItems] = useState(false)

  useEffect(() => {
    Promise.all([
      warehousesApi.list(),
      itemsApi.list({ pageSize: 200, status: "Active" }),
      uomsApi.list(),
      vendorsApi.list({ pageSize: 200, status: "Active" }),
      purchaseOrdersApi.list({ pageSize: 200 }),
    ])
      .then(([wh, it, uo, ve, po]) => {
        setWarehouses(wh.items)
        setItems(it.items)
        setUoms(uo.items)
        setVendors(ve.items)
        setPurchaseOrders(po.items.filter((p) => p.status === "Approved" || p.status === "PartiallyReceived"))
      })
      .catch((err) => setLoadError(errorMessage(err)))
  }, [])

  useEffect(() => {
    if (!warehouseId) {
      setBins([])
      return
    }
    warehousesApi.listBins(warehouseId).then(setBins).catch(() => setBins([]))
  }, [warehouseId])

  function switchMode(next: Mode) {
    setMode(next)
    setPoId(null)
    setVendorId(null)
    setLines([emptyLine()])
    setHeaderError(null)
    setLineErrors({})
  }

  async function handleSelectPo(nextPoId: number | null) {
    setPoId(nextPoId)
    if (!nextPoId) {
      setLines([emptyLine()])
      return
    }

    setPoLoading(true)
    setHeaderError(null)
    try {
      const po: PurchaseOrder = await purchaseOrdersApi.get(nextPoId)
      const openLines = po.lines.filter((l) => l.qtyReceived < l.qty)
      if (openLines.length === 0) {
        setHeaderError("This purchase order has no open (unreceived) lines.")
        setLines([])
        return
      }
      setWarehouseId((prev) => prev ?? openLines[0].warehouseId)
      setLines(
        openLines.map(
          (l): DraftLine => ({
            key: newKey(),
            poLineId: l.poLineId,
            itemId: l.itemId,
            binId: null,
            qty: String(l.qty - l.qtyReceived),
            unitCost: String(l.unitPrice),
            poUnitPrice: l.unitPrice,
            discountPct: "0",
            vatPct: "0",
            holdStatus: "Available",
            batchNo: "",
            expiryDate: "",
            serialNumbersText: "",
          })
        )
      )
    } catch (err) {
      setHeaderError(errorMessage(err))
    } finally {
      setPoLoading(false)
    }
  }

  // Re-pull the active items list so an item created in the other tab (via "New item")
  // becomes selectable without reloading the whole screen and losing the in-progress GRN.
  async function refreshItems() {
    setRefreshingItems(true)
    try {
      const res = await itemsApi.list({ pageSize: 200, status: "Active" })
      setItems(res.items)
      toast.success("Items refreshed", `${res.items.length} active item${res.items.length === 1 ? "" : "s"} loaded.`)
    } catch (err) {
      toast.error("Could not refresh items", errorMessage(err))
    } finally {
      setRefreshingItems(false)
    }
  }

  function updateLine(key: string, patch: Partial<DraftLine>) {
    setLines((prev) => prev.map((l) => (l.key === key ? { ...l, ...patch } : l)))
  }

  function removeLine(key: string) {
    setLines((prev) => prev.filter((l) => l.key !== key))
  }

  function itemFor(itemId: number | null) {
    return items?.find((i) => i.itemId === itemId) ?? null
  }

  async function handleSubmit() {
    setSubmitError(null)
    setHeaderError(null)

    const header = grnHeaderSchema.safeParse({
      warehouseId: warehouseId ?? 0,
      vendorId: mode === "direct" ? vendorId : null,
      poId: mode === "po" ? poId : null,
    })
    if (!header.success) {
      setHeaderError(header.error.issues[0]?.message ?? "Check the header fields.")
      return
    }
    if (mode === "direct" && !vendorId) {
      setHeaderError("Select a vendor for a direct receipt.")
      return
    }
    if (mode === "po" && !poId) {
      setHeaderError("Select a purchase order.")
      return
    }
    if (lines.length === 0) {
      setSubmitError("Add at least one line.")
      return
    }

    const nextLineErrors: Record<string, Record<string, string>> = {}
    for (const line of lines) {
      const errors = validateLine({
        itemId: line.itemId,
        qty: line.qty,
        unitCost: line.unitCost,
        discountPct: line.discountPct,
        vatPct: line.vatPct,
        trackingMode: itemFor(line.itemId)?.trackingMode ?? null,
        batchNo: line.batchNo,
        serialNumbersText: line.serialNumbersText,
      })
      if (Object.keys(errors).length > 0) nextLineErrors[line.key] = errors
    }
    setLineErrors(nextLineErrors)
    if (Object.keys(nextLineErrors).length > 0) {
      setSubmitError("Fix the highlighted lines before submitting.")
      return
    }

    const payloadLines: CreateGrnLineInput[] = lines.map((l) => {
      const trackingMode = itemFor(l.itemId)?.trackingMode ?? "None"
      return {
        poLineId: l.poLineId,
        itemId: l.itemId as number,
        binId: l.binId,
        qty: Number(l.qty),
        unitCost: Number(l.unitCost),
        discountPct: Number(l.discountPct) || 0,
        vatPct: Number(l.vatPct) || 0,
        holdStatus: l.holdStatus,
        batch: trackingMode === "Batch" ? { batchNo: l.batchNo.trim(), expiryDate: l.expiryDate || null } : null,
        serialNumbers: trackingMode === "Serial" ? splitSerials(l.serialNumbersText) : null,
      }
    })

    setSubmitting(true)
    try {
      const grn = await grnsApi.create({
        poId: mode === "po" ? poId : null,
        vendorId: mode === "direct" ? vendorId : null,
        warehouseId: warehouseId as number,
        lines: payloadLines,
      })
      toast.success("GRN created", `${grn.docNo} is ready to confirm.`)
      router.push(`/dashboard/receiving/grn/${grn.grnId}`)
    } catch (err) {
      setSubmitError(errorMessage(err))
      toast.error("Could not create GRN", errorMessage(err))
    } finally {
      setSubmitting(false)
    }
  }

  const loading = !warehouses || !items || !uoms || !vendors || !purchaseOrders

  return (
    <div className="flex flex-col gap-6">
      <div className="flex items-center gap-3">
        <div>
          <h1 className="text-2xl font-bold text-foreground">New GRN</h1>
          <p className="text-base text-muted-foreground">Receive goods against a purchase order, or record a direct receipt (FR-GRN-01/02).</p>
        </div>
      </div>

      {loadError && (
        <div className="rounded-lg border border-destructive/30 bg-destructive/5 p-5 text-base text-destructive">{loadError}</div>
      )}

      {loading && !loadError && <Skeleton className="h-12 w-full" />}

      {!loading && (
        <>
          <div className="flex w-fit gap-2 rounded-full border border-input bg-muted/40 p-1">
            <Button
              type="button"
              size="sm"
              variant={mode === "po" ? "default" : "ghost"}
              className="rounded-full"
              onClick={() => switchMode("po")}
            >
              Against PO
            </Button>
            <Button
              type="button"
              size="sm"
              variant={mode === "direct" ? "default" : "ghost"}
              className="rounded-full"
              onClick={() => switchMode("direct")}
            >
              Direct receipt
            </Button>
          </div>

          <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
            {mode === "po" && (
              <div className="flex flex-col gap-2 sm:col-span-2">
                <Label className="text-base">Purchase order</Label>
                <Select<number | null> value={poId} onValueChange={(v) => handleSelectPo(v)}>
                  <SelectTrigger className="h-12! w-full text-base">
                    <SelectValue placeholder="Select an approved/partially received PO" />
                  </SelectTrigger>
                  <SelectContent>
                    {(purchaseOrders ?? []).map((p) => (
                      <SelectItem key={p.poId} value={p.poId} className="text-base">
                        {p.docNo} — Vendor #{p.vendorId} ({p.status})
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>
            )}

            {mode === "direct" && (
              <div className="flex flex-col gap-2 sm:col-span-2">
                <Label className="text-base">Vendor</Label>
                <Select<number | null> value={vendorId} onValueChange={(v) => setVendorId(v)}>
                  <SelectTrigger className="h-12! w-full text-base">
                    <SelectValue placeholder="Select vendor" />
                  </SelectTrigger>
                  <SelectContent>
                    {(vendors ?? []).map((v) => (
                      <SelectItem key={v.vendorId} value={v.vendorId} className="text-base">
                        {v.code} — {v.name}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>
            )}

            <div className="flex flex-col gap-2">
              <Label className="text-base">Warehouse</Label>
              <Select<number | null> value={warehouseId} onValueChange={(v) => setWarehouseId(v)}>
                <SelectTrigger className="h-12! w-full text-base">
                  <SelectValue placeholder="Select warehouse" />
                </SelectTrigger>
                <SelectContent>
                  {(warehouses ?? []).map((w) => (
                    <SelectItem key={w.warehouseId} value={w.warehouseId} className="text-base">
                      {w.code} — {w.name}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
          </div>

          {headerError && (
            <div className="rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-base text-destructive">{headerError}</div>
          )}

          <div className="flex flex-col gap-3">
            <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
              <div>
                <h2 className="text-base font-semibold text-foreground">Lines</h2>
                {mode === "po" && (
                  <p className="text-sm text-muted-foreground">
                    PO lines are prefilled. Use “Add line” to receive an item that isn’t on the PO.
                  </p>
                )}
              </div>
              <div className="flex flex-wrap items-center gap-2">
                {/* Off-PO items are allowed on a PO-based GRN — the server treats a line with
                    no poLineId as a direct receipt (docs/10 FR-GRN-01, revised). */}
                <Button type="button" variant="outline" onClick={() => setLines((prev) => [...prev, emptyLine()])}>
                  <Plus className="size-5" />
                  Add line
                </Button>
                {/* Create a brand-new item in a separate tab, then refresh to pick it up. */}
                <Button
                  type="button"
                  variant="outline"
                  onClick={() => window.open("/dashboard/products/new", "_blank", "noopener,noreferrer")}
                >
                  <ExternalLink className="size-5" />
                  New item
                </Button>
                <Button
                  type="button"
                  variant="outline"
                  size="icon"
                  onClick={refreshItems}
                  disabled={refreshingItems}
                  aria-label="Refresh items"
                  title="Refresh items"
                >
                  <RefreshCw className={cn("size-5", refreshingItems && "animate-spin")} />
                </Button>
              </div>
            </div>

            {poLoading && <Skeleton className="h-24 w-full" />}

            {!poLoading && lines.length > 0 && (
              <div className="overflow-x-auto">
                <Table className="text-base">
                  <TableHeader>
                    <TableRow>
                      <TableHead className="h-12 w-56 px-3 text-sm">Item</TableHead>
                      <TableHead className="h-12 w-24 px-3 text-sm">UOM</TableHead>
                      <TableHead className="h-12 w-28 px-3 text-sm">Bin</TableHead>
                      <TableHead className="h-12 w-32 px-3 text-sm">Qty</TableHead>
                      <TableHead className="h-12 w-28 px-3 text-sm">Unit cost</TableHead>
                      <TableHead className="h-12 w-24 px-3 text-sm">Disc %</TableHead>
                      <TableHead className="h-12 w-24 px-3 text-sm">VAT %</TableHead>
                      <TableHead className="h-12 w-28 px-3 text-sm text-right">Line total</TableHead>
                      <TableHead className="h-12 w-56 px-3 text-sm">Hold status</TableHead>
                      <TableHead className="h-12 w-44 px-3 text-sm">Batch / Serial</TableHead>
                      <TableHead className="h-12 w-10 px-3" />
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {lines.map((line) => {
                      const item = itemFor(line.itemId)
                      const errors = lineErrors[line.key] ?? {}
                      return (
                        <TableRow key={line.key}>
                          <TableCell className="px-3 py-3 align-top">
                            {line.poLineId ? (
                              <div className="flex h-11 items-center text-base">
                                {item ? `${item.sku} — ${item.name}` : `Item #${line.itemId}`}
                              </div>
                            ) : (
                              <>
                                <Select<number | null> value={line.itemId} onValueChange={(v) => updateLine(line.key, { itemId: v })}>
                                  <SelectTrigger className="h-11! w-full text-base" aria-invalid={!!errors.itemId}>
                                    <SelectValue placeholder="Select item" />
                                  </SelectTrigger>
                                  <SelectContent>
                                    {(items ?? []).map((i) => (
                                      <SelectItem key={i.itemId} value={i.itemId} className="text-base">
                                        {i.sku} — {i.name}
                                      </SelectItem>
                                    ))}
                                  </SelectContent>
                                </Select>
                                <FieldError errors={[errors.itemId ? { message: errors.itemId } : undefined]} />
                              </>
                            )}
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            <div className="flex h-11 items-center text-base text-muted-foreground">
                              {baseUomLabel(items ?? [], uoms ?? [], line.itemId)}
                            </div>
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            <Select<number | null> value={line.binId} onValueChange={(v) => updateLine(line.key, { binId: v })}>
                              <SelectTrigger className="h-11! w-full text-base">
                                <SelectValue placeholder="None" />
                              </SelectTrigger>
                              <SelectContent>
                                {bins.map((b) => (
                                  <SelectItem key={b.binId} value={b.binId} className="text-base">
                                    {b.code}
                                  </SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            <Input
                              type="number"
                              min="0"
                              step="any"
                              value={line.qty}
                              aria-invalid={!!errors.qty}
                              onChange={(e) => updateLine(line.key, { qty: e.target.value })}
                              className="h-11 text-base"
                            />
                            <FieldError errors={[errors.qty ? { message: errors.qty } : undefined]} />
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            <Input
                              type="number"
                              min="0"
                              step="any"
                              value={line.unitCost}
                              aria-invalid={!!errors.unitCost}
                              onChange={(e) => updateLine(line.key, { unitCost: e.target.value })}
                              className="h-11 text-base"
                            />
                            <FieldError errors={[errors.unitCost ? { message: errors.unitCost } : undefined]} />
                            {line.poUnitPrice !== null && Number(line.unitCost) !== line.poUnitPrice && (
                              <p className="mt-1 text-xs text-warning">
                                PO price {line.poUnitPrice.toFixed(2)} — variance recorded
                              </p>
                            )}
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            <Input
                              type="number"
                              min="0"
                              max="100"
                              step="any"
                              value={line.discountPct}
                              aria-invalid={!!errors.discountPct}
                              onChange={(e) => updateLine(line.key, { discountPct: e.target.value })}
                              className="h-11 text-base"
                            />
                            <FieldError errors={[errors.discountPct ? { message: errors.discountPct } : undefined]} />
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            <Input
                              type="number"
                              min="0"
                              max="100"
                              step="any"
                              value={line.vatPct}
                              aria-invalid={!!errors.vatPct}
                              onChange={(e) => updateLine(line.key, { vatPct: e.target.value })}
                              className="h-11 text-base"
                            />
                            <FieldError errors={[errors.vatPct ? { message: errors.vatPct } : undefined]} />
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top text-right tabular-nums">
                            {(() => {
                              const c = computeLine(line)
                              return (
                                <div className="flex h-11 flex-col justify-center">
                                  <span>{c.lineTotal.toFixed(2)}</span>
                                  <span className="text-xs text-muted-foreground">
                                    net {c.receivedValue.toFixed(2)} + VAT {c.vatAmount.toFixed(2)}
                                  </span>
                                </div>
                              )
                            })()}
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            <Select<HoldStatus>
                              value={line.holdStatus}
                              onValueChange={(v) => v && updateLine(line.key, { holdStatus: v })}
                            >
                              <SelectTrigger className="h-11! w-full text-base">
                                <SelectValue />
                              </SelectTrigger>
                              <SelectContent>
                                <SelectItem value="Available" className="text-base">Available</SelectItem>
                                <SelectItem value="OnHold" className="text-base">On hold (inspection)</SelectItem>
                              </SelectContent>
                            </Select>
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            {item?.trackingMode === "Batch" && (
                              <div className="flex flex-col gap-1.5">
                                <Input
                                  placeholder="Batch no."
                                  value={line.batchNo}
                                  aria-invalid={!!errors.batchNo}
                                  onChange={(e) => updateLine(line.key, { batchNo: e.target.value })}
                                  className="h-9 text-sm"
                                />
                                <Input
                                  type="date"
                                  value={line.expiryDate}
                                  onChange={(e) => updateLine(line.key, { expiryDate: e.target.value })}
                                  className="h-9 text-sm"
                                />
                                <FieldError errors={[errors.batchNo ? { message: errors.batchNo } : undefined]} />
                              </div>
                            )}
                            {item?.trackingMode === "Serial" && (
                              <div className="flex flex-col gap-1.5">
                                <textarea
                                  placeholder="One serial per line"
                                  value={line.serialNumbersText}
                                  aria-invalid={!!errors.serialNumbers}
                                  onChange={(e) => updateLine(line.key, { serialNumbersText: e.target.value })}
                                  className="min-h-16 w-full rounded-lg border border-input bg-transparent p-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
                                />
                                <FieldError errors={[errors.serialNumbers ? { message: errors.serialNumbers } : undefined]} />
                              </div>
                            )}
                            {(!item || item.trackingMode === "None") && (
                              <span className="text-sm text-muted-foreground">—</span>
                            )}
                          </TableCell>
                          <TableCell className="px-3 py-3 align-top">
                            <Button type="button" variant="ghost" size="icon" onClick={() => removeLine(line.key)} aria-label="Remove line">
                              <Trash2 className="size-5" />
                            </Button>
                          </TableCell>
                        </TableRow>
                      )
                    })}
                  </TableBody>
                </Table>
              </div>
            )}

            {!poLoading && lines.length > 0 && (
              <div className="flex justify-end gap-3 border-t border-border pt-4 text-base">
                <span className="text-muted-foreground">Document total (incl. VAT)</span>
                <span className="font-semibold tabular-nums">
                  {lines.reduce((sum, l) => sum + computeLine(l).lineTotal, 0).toFixed(2)}
                </span>
              </div>
            )}
          </div>

          {submitError && (
            <div className="rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-base text-destructive">{submitError}</div>
          )}

          <div className="flex justify-end gap-3">
            <Link href="/dashboard/receiving/grn" className={cn(buttonVariants({ variant: "outline", size: "lg" }))}>
              Cancel
            </Link>
            <Button size="lg" type="button" onClick={handleSubmit} disabled={submitting}>
              {submitting ? "Creating…" : "Create GRN"}
            </Button>
          </div>
        </>
      )}
    </div>
  )
}
