"use client";

import * as React from "react";

import { Example } from "@/components/design-system/example";
import { Section } from "@/components/design-system/section";
import { EditableGrid, type GridColumn, type GridRow } from "@/components/medora/editable-grid";
import { useUnsavedChangesGuard } from "@/components/medora/unsaved-changes";
import { toast } from "@/components/ui/toast";
import { products, TODAY } from "@/lib/mock-data";
import { cn } from "@/lib/utils";

const UNIT: Record<string, string> = { Tablet: "tablets", Capsule: "capsules", Syrup: "bottles", Cream: "tubes", Inhaler: "inhalers", Box: "boxes", Sachet: "sachets", Device: "units", Pack: "packs" };
const isWhole = (v: string) => /^\d+$/.test(v);

/** Grid data that behaves like the API: drafts save into committed values, derived cells refresh after edits. */
export function useGridData(initial: GridRow[]) {
  const [rows, setRows] = React.useState(initial);
  const [draft, setDraft] = React.useState<Record<string, Record<string, string>>>({});
  const [updating, setUpdating] = React.useState<Set<string>>(new Set());
  const [saving, setSaving] = React.useState(false);

  const onDraftChange = (rowId: string, key: string, value: string | undefined) => {
    setDraft((d) => {
      const row = { ...(d[rowId] ?? {}) };
      if (value === undefined) delete row[key];
      else row[key] = value;
      const next = { ...d, [rowId]: row };
      if (!Object.keys(row).length) delete next[rowId];
      return next;
    });
    // Derived values (variance, value) come back from the API after a committed edit (spec P2).
    setUpdating((u) => new Set(u).add(rowId));
    setTimeout(
      () =>
        setUpdating((u) => {
          const n = new Set(u);
          n.delete(rowId);
          return n;
        }),
      450,
    );
  };
  const saveDraft = async () => {
    setSaving(true);
    await new Promise((r) => setTimeout(r, 700));
    setRows((rs) => rs.map((r) => (draft[r.id] ? { ...r, values: { ...r.values, ...draft[r.id] } } : r)));
    setDraft({});
    setSaving(false);
    toast.add({ type: "success", title: "Draft saved", description: "Nothing is applied to stock until the count is reviewed and applied." });
  };
  return { rows, draft, setDraft, updating, saving, onDraftChange, saveDraft, discard: () => setDraft({}) };
}

/* ------------------------------------ Stock take ------------------------------------ */

const countRows: GridRow[] = products
  .filter((p) => p.type === "Product")
  .slice(0, 10)
  .map((p) => ({ id: String(p.id), header: `${p.name} ${p.strength}`, sub: `Shelf ${p.shelf} · ${p.barcode}`, values: { system: String(p.stock), counted: "", note: "", unit: UNIT[p.form] ?? "units", cost: String(p.costPrice) } }));

function variance(row: Record<string, string>) {
  if (!isWhole(row.counted ?? "")) return null;
  return Number(row.counted) - Number(row.system);
}

const countColumns: GridColumn[] = [
  { key: "system", header: "System", kind: "read", align: "right", render: (r) => `${Number(r.system).toLocaleString("en-US")} ${r.unit}` },
  {
    key: "counted",
    header: "Counted",
    kind: "number",
    width: "9rem",
    placeholder: "Count",
    unit: (row) => row.values.unit,
    validate: (v) => (v === "" ? undefined : !isWhole(v) ? "Enter a whole number of units, 0 or more." : Number(v) > 100000 ? "That is more than 100,000. Check the count." : undefined),
  },
  {
    key: "variance",
    header: "Variance",
    kind: "derived",
    render: (r) => {
      const d = variance(r);
      if (d === null) return <span className="text-muted-foreground">–</span>;
      if (d === 0) return <span className="text-muted-foreground">No change</span>;
      return (
        <span className={cn("font-medium", d < 0 ? "text-status-warning-text" : "text-status-info-text")}>
          {d > 0 ? "+" : "−"}
          {Math.abs(d).toLocaleString("en-US")}
          <span className="ml-1 font-normal">{d > 0 ? "over" : "short"}</span>
        </span>
      );
    },
  },
  {
    key: "value",
    header: "Value at cost",
    kind: "derived",
    render: (r) => {
      const d = variance(r);
      return d === null || d === 0 ? <span className="text-muted-foreground">–</span> : `${d < 0 ? "−" : "+"}KES ${(Math.abs(d) * Number(r.cost)).toFixed(2)}`;
    },
  },
  { key: "note", header: "Note", kind: "text", width: "12rem", placeholder: "Optional", validate: (v, row) => (variance(row) !== null && Math.abs(variance(row)!) >= 20 && !v.trim() ? "A difference of 20 or more needs a note." : undefined) },
];

function StockTakeGrid() {
  const g = useGridData(countRows);
  const dirty = Object.keys(g.draft).length > 0;
  const { dialog } = useUnsavedChangesGuard({ dirty, changes: ["counts"], save: async () => {
      await g.saveDraft();
      return true;
    }, discard: g.discard });
  const counted = g.rows.filter((r) => (g.draft[r.id]?.counted ?? r.values.counted) !== "").length;
  return (
    <>
      <EditableGrid
        caption="Stock take counts, Westlands Main"
        columns={countColumns}
        rows={g.rows}
        draft={g.draft}
        updating={g.updating}
        saving={g.saving}
        onDraftChange={g.onDraftChange}
        onSaveDraft={g.saveDraft}
        onDiscard={g.discard}
        csv={{
          filename: "stock-take-template.csv",
          template: ["barcode,counted", ...countRows.map((r) => `${r.sub!.split("· ")[1]},`)].join("\n"),
          onUpload: (text) => {
            const skipped: string[] = [];
            let applied = 0;
            text
              .split(/\r?\n/)
              .slice(1)
              .filter((l) => l.trim())
              .forEach((line, i) => {
                const [code, count] = line.split(",").map((s) => s.trim());
                const row = countRows.find((r) => r.sub!.endsWith(code));
                if (!row) return skipped.push(`line ${i + 2}: unknown barcode ${code}`);
                if (!count) return;
                g.onDraftChange(row.id, "counted", count);
                applied++;
              });
            return { applied, skipped };
          },
        }}
        footer={
          <tr className="border-t border-border bg-muted/50 text-sm">
            <th scope="row" className="px-3 py-2 text-left font-medium" colSpan={6}>
              {counted} of {g.rows.length} lines counted
            </th>
          </tr>
        }
      />
      {dialog}
    </>
  );
}

/* ------------------------------------ Receiving ------------------------------------- */

export const grnRows: GridRow[] = [
  { id: "g1", header: "Amoxil 500 mg", sub: "PO-2041 · ordered 200 capsules", values: { ordered: "200", received: "200", batch: "AM25120", expiry: "2028-03-31", cost: "9.00" } },
  { id: "g2", header: "Augmentin 625 mg", sub: "PO-2041 · ordered 140 tablets", values: { ordered: "140", received: "", batch: "", expiry: "", cost: "55.00" } },
  { id: "g3", header: "Ventolin Inhaler 100 mcg", sub: "PO-2041 · ordered 12 inhalers", values: { ordered: "12", received: "", batch: "", expiry: "", cost: "420.00" } },
  { id: "g4", header: "Benylin Cough Syrup 100 ml", sub: "PO-2041 · ordered 24 bottles", values: { ordered: "24", received: "", batch: "", expiry: "", cost: "210.00" } },
];

export const grnColumns: GridColumn[] = [
  { key: "ordered", header: "Ordered", kind: "read", align: "right" },
  {
    key: "received",
    header: "Received",
    kind: "number",
    width: "7rem",
    placeholder: "0",
    validate: (v, row) => (v === "" ? undefined : !isWhole(v) ? "Enter a whole number." : Number(v) > Number(row.ordered) ? `More than the ${row.ordered} ordered. Receive the extra on a new PO or correct the count.` : undefined),
  },
  { key: "batch", header: "Batch", kind: "text", width: "8rem", placeholder: "Batch no.", validate: (v, row) => (Number(row.received || 0) > 0 && !v.trim() ? "Enter the batch number from the pack." : undefined) },
  {
    key: "expiry",
    header: "Expiry",
    kind: "date",
    width: "10rem",
    validate: (v, row) => (Number(row.received || 0) > 0 && !v ? "Enter the expiry date from the pack." : v && v <= TODAY ? "Expiry date is in the past. Check the date on the pack." : undefined),
  },
  { key: "cost", header: "Unit cost", kind: "number", width: "7rem", validate: (v) => (v !== "" && !/^\d+(\.\d{1,2})?$/.test(v) ? "Enter an amount like 55.00." : undefined) },
  {
    key: "line",
    header: "Line value",
    kind: "derived",
    render: (r) => (isWhole(r.received ?? "") && r.cost ? `KES ${(Number(r.received) * Number(r.cost)).toLocaleString("en-US", { minimumFractionDigits: 2 })}` : <span className="text-muted-foreground">–</span>),
  },
];

function ReceivingGrid() {
  const g = useGridData(grnRows);
  const dirty = Object.keys(g.draft).length > 0;
  const { dialog } = useUnsavedChangesGuard({ dirty, changes: ["receiving lines"], discard: g.discard });
  return (
    <>
      <EditableGrid caption="Goods received against PO-2041" columns={grnColumns} rows={g.rows} draft={g.draft} updating={g.updating} saving={g.saving} onDraftChange={g.onDraftChange} onSaveDraft={g.saveDraft} onDiscard={g.discard} />
      {dialog}
    </>
  );
}

export function EditableGridSection() {
  return (
    <Section
      id="editable-grid"
      title="Editable grid"
      description="For entering many lines in one sitting: stock take counts, goods receiving, branch transfers, payroll lines. A native table with real inputs, each named by its column and row. Enter commits and moves down the column; ↑/↓ commit and move; Esc reverts the cell; Ctrl/Cmd+S saves the draft; Tab moves across. Number cells never change on arrow keys. Changed cells show a dot until saved; errors show on the cell and in a summary that links to it. Derived values wait for the API. Nothing changes stock until the draft is reviewed and applied."
    >
      <Example title="Stock take counts" description="Type counts going down with Enter; try 25 on a line with no note, or upload a CSV from the template">
        <StockTakeGrid />
      </Example>
      <Example title="Goods receiving (GRN)" description="Receiving a quantity makes batch and expiry required; a past expiry or more than ordered is an error">
        <ReceivingGrid />
      </Example>
    </Section>
  );
}
