"use client";

import * as React from "react";
import { ArrowDownRightIcon, ArrowUpRightIcon, SearchIcon } from "lucide-react";

import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { Switch } from "@/components/ui/switch";
import { cn } from "@/lib/utils";

/*
 * Stock per branch and location (PRD 3.2, IN-09, IN-11; Phase 1 item 1.2). Stock lives at a location
 * inside a branch (Dispensary, Store room, Front shop). Reorder levels apply to the branch total.
 * Values at cost come from batches (passed in as a weighted cost per row), never product.costPrice alone.
 */

export type StockLocation = { id: string; name: string };
export type StockBranch = { id: number; name: string; locations: StockLocation[] };

export type StockRow = {
  productId: number;
  name: string;
  /** Strength · form, shown under the name. */
  sub?: string;
  category: string;
  subCategory?: string;
  brand?: string;
  /** Weighted cost from batches, per unit. */
  unitCost: number;
  unitPrice: number;
  /** Reorder level per branch. */
  reorderLevel: number;
  /** Quantity per location id. Missing means 0. Negative is possible (sold before receiving was recorded). */
  qty: Record<string, number>;
};

export type StockState = "all" | "in-stock" | "low" | "zero-negative";

const n0 = new Intl.NumberFormat("en-US");
const n2 = new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });

export const branchQty = (row: StockRow, branch: StockBranch, locationName?: string) =>
  branch.locations.filter((l) => !locationName || l.name === locationName).reduce((s, l) => s + (row.qty[l.id] ?? 0), 0);

export type CellStatus = "negative" | "out" | "low" | "ok";
export const cellStatus = (q: number, reorder: number): CellStatus => (q < 0 ? "negative" : q === 0 ? "out" : q <= reorder ? "low" : "ok");

/** A quantity with its state as a word, so it doesn't rely on colour (spec: status is never colour alone). */
export function QtyCell({ q, status, strong = false, negativeInside = false }: { q: number; status: CellStatus; strong?: boolean; /** A branch total whose own locations include a negative. */ negativeInside?: boolean }) {
  return (
    <span className="inline-flex flex-col items-end leading-tight">
      <span className={cn("tabular-nums", strong && "font-semibold", status === "negative" && "text-status-error-text")}>{q < 0 ? `−${n0.format(-q)}` : n0.format(q)}</span>
      {status !== "ok" ? (
        <span className={cn("text-[11px] font-medium", status === "negative" || status === "out" ? "text-status-error-text" : "text-status-warning-text")}>
          {status === "negative" ? "Negative" : status === "out" ? "Out" : "Low"}
        </span>
      ) : null}
      {negativeInside && status !== "negative" ? <span className="text-[11px] font-medium text-status-error-text">Negative at a location</span> : null}
    </span>
  );
}

/**
 * Branch stock matrix: products down, branches across (or one branch's locations, for a user scoped to a
 * branch). "Show locations" splits every branch into its locations. Filters: search, category, sub-category, brand, location,
 * stock state. The totals strip follows the filters: items, units, value at cost and at selling price.
 * Clicking a branch cell calls `onOpenCell` (detail with batches and other branches' stock).
 */
export function BranchStockMatrix({
  rows,
  branches,
  scopeBranchId,
  onOpenCell,
  caption,
}: {
  rows: StockRow[];
  branches: StockBranch[];
  /** Set when the user may see only their own branch (no "All branches" permission). */
  scopeBranchId?: number;
  onOpenCell?: (row: StockRow, branch: StockBranch) => void;
  caption: string;
}) {
  const visible = scopeBranchId ? branches.filter((b) => b.id === scopeBranchId) : branches;
  const [query, setQuery] = React.useState("");
  const [category, setCategory] = React.useState("");
  const [subCategory, setSubCategory] = React.useState("");
  const [brand, setBrand] = React.useState("");
  const [location, setLocation] = React.useState("");
  const [state, setState] = React.useState<StockState>("all");
  const [split, setSplit] = React.useState(!!scopeBranchId);
  const showLocations = split || visible.length === 1;

  const categories = [...new Set(rows.map((r) => r.category))].sort();
  // Sub-categories narrow to the chosen category, so the list never offers a combination with no products.
  const subCategories = [...new Set(rows.filter((r) => !category || r.category === category).map((r) => r.subCategory).filter((x): x is string => !!x))].sort();
  const brands = [...new Set(rows.map((r) => r.brand).filter((x): x is string => !!x))].sort();
  const locationNames = [...new Set(visible.flatMap((b) => b.locations.map((l) => l.name)))];
  const loc = location || undefined;

  const total = (r: StockRow) => visible.reduce((s, b) => s + branchQty(r, b, loc), 0);
  const statesOf = (r: StockRow) => visible.map((b) => cellStatus(branchQty(r, b, loc), loc ? 0 : r.reorderLevel));
  // Negative at any location matters even when the branch total is positive: stock left without a record.
  const hasNegative = (r: StockRow) => visible.some((b) => b.locations.some((l) => (!loc || l.name === loc) && (r.qty[l.id] ?? 0) < 0));

  const shown = rows.filter((r) => {
    const q = query.trim().toLowerCase();
    if (q && !`${r.name} ${r.sub ?? ""}`.toLowerCase().includes(q)) return false;
    if (category && r.category !== category) return false;
    if (subCategory && r.subCategory !== subCategory) return false;
    if (brand && r.brand !== brand) return false;
    const st = statesOf(r);
    if (state === "in-stock") return total(r) > 0;
    if (state === "low") return st.includes("low") || st.includes("out");
    if (state === "zero-negative") return st.includes("out") || st.includes("negative") || hasNegative(r);
    return true;
  });

  const units = shown.reduce((s, r) => s + Math.max(0, total(r)), 0);
  const atCost = shown.reduce((s, r) => s + Math.max(0, total(r)) * r.unitCost, 0);
  const atPrice = shown.reduce((s, r) => s + Math.max(0, total(r)) * r.unitPrice, 0);
  const negatives = shown.filter(hasNegative).length;

  const head = "px-3 py-2 text-xs font-medium text-muted-foreground";
  return (
    <div className="space-y-3">
      {/* Filters */}
      <div className="flex flex-wrap items-end gap-2">
        <div className="relative w-full sm:w-60">
          <SearchIcon className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" aria-hidden />
          <Input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Find a product" aria-label="Find a product" className="pl-8" />
        </div>
        <NativeSelect
          aria-label="Category"
          value={category}
          onChange={(e) => {
            setCategory(e.target.value);
            setSubCategory("");
          }}
        >
          <NativeSelectOption value="">All categories</NativeSelectOption>
          {categories.map((c) => (
            <NativeSelectOption key={c} value={c}>
              {c}
            </NativeSelectOption>
          ))}
        </NativeSelect>
        {subCategories.length ? (
          <NativeSelect aria-label="Sub-category" value={subCategory} onChange={(e) => setSubCategory(e.target.value)}>
            <NativeSelectOption value="">All sub-categories</NativeSelectOption>
            {subCategories.map((c) => (
              <NativeSelectOption key={c} value={c}>
                {c}
              </NativeSelectOption>
            ))}
          </NativeSelect>
        ) : null}
        {brands.length ? (
          <NativeSelect aria-label="Brand" value={brand} onChange={(e) => setBrand(e.target.value)}>
            <NativeSelectOption value="">All brands</NativeSelectOption>
            {brands.map((c) => (
              <NativeSelectOption key={c} value={c}>
                {c}
              </NativeSelectOption>
            ))}
          </NativeSelect>
        ) : null}
        <NativeSelect aria-label="Location" value={location} onChange={(e) => setLocation(e.target.value)}>
          <NativeSelectOption value="">All locations</NativeSelectOption>
          {locationNames.map((l) => (
            <NativeSelectOption key={l} value={l}>
              {l}
            </NativeSelectOption>
          ))}
        </NativeSelect>
        <NativeSelect aria-label="Stock" value={state} onChange={(e) => setState(e.target.value as StockState)}>
          <NativeSelectOption value="all">Any stock</NativeSelectOption>
          <NativeSelectOption value="in-stock">In stock</NativeSelectOption>
          <NativeSelectOption value="low">Low or out somewhere</NativeSelectOption>
          <NativeSelectOption value="zero-negative">Zero or negative</NativeSelectOption>
        </NativeSelect>
        {visible.length > 1 ? (
          <label className="ml-auto flex items-center gap-2 text-sm">
            <Switch checked={split} onCheckedChange={setSplit} /> Show locations
          </label>
        ) : null}
      </div>

      {/* Totals follow the filters */}
      <dl className="grid grid-cols-2 gap-px overflow-hidden rounded-lg border border-[var(--color-border-panel)] bg-[var(--color-border-panel)] text-sm sm:grid-cols-4">
        {[
          ["Items", n0.format(shown.length)],
          ["Units", n0.format(units)],
          ["Value at cost", `KES ${n2.format(atCost)}`],
          ["Value at selling price", `KES ${n2.format(atPrice)}`],
        ].map(([k, v]) => (
          <div key={k} className="bg-card px-3 py-2">
            <dt className="text-xs text-muted-foreground">{k}</dt>
            <dd className="font-semibold tabular-nums">{v}</dd>
          </div>
        ))}
      </dl>
      <p className="text-xs text-muted-foreground">
        {visible.length === 1 ? visible[0].name : `${visible.length} branches`}
        {loc ? ` · ${loc} only` : ""}. Value at cost is from batches; negative stock counts as zero in the values
        {negatives ? `; ${negatives} ${negatives === 1 ? "item has" : "items have"} negative stock to investigate` : ""}.
      </p>

      {/* Matrix */}
      <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
        <table className="w-full border-collapse text-sm">
          <caption className="sr-only">{caption}</caption>
          <thead className="bg-[var(--color-bg-surface-subtle)]">
            {showLocations ? (
              <tr className="border-b border-[var(--color-border-subtle)]">
                <th rowSpan={2} scope="col" className={cn(head, "sticky left-0 z-[1] bg-[var(--color-bg-surface-subtle)] text-left align-bottom")}>
                  Product
                </th>
                {visible.map((b) => (
                  <th key={b.id} scope="colgroup" colSpan={b.locations.filter((l) => !loc || l.name === loc).length + 1} className={cn(head, "border-l border-[var(--color-border-subtle)] text-center")}>
                    {b.name}
                  </th>
                ))}
                {visible.length > 1 ? (
                  <th rowSpan={2} scope="col" className={cn(head, "border-l border-[var(--color-border-subtle)] text-right align-bottom")}>
                    All branches
                  </th>
                ) : null}
              </tr>
            ) : null}
            <tr className="border-b border-[var(--color-border-panel)]">
              {!showLocations ? (
                <th scope="col" className={cn(head, "sticky left-0 z-[1] bg-[var(--color-bg-surface-subtle)] text-left")}>
                  Product
                </th>
              ) : null}
              {visible.map((b) =>
                showLocations ? (
                  <React.Fragment key={b.id}>
                    {b.locations
                      .filter((l) => !loc || l.name === loc)
                      .map((l, i) => (
                        <th key={l.id} scope="col" className={cn(head, "text-right", i === 0 && "border-l border-[var(--color-border-subtle)]")}>
                          {l.name}
                        </th>
                      ))}
                    <th scope="col" className={cn(head, "text-right font-semibold text-foreground")}>
                      Total
                    </th>
                  </React.Fragment>
                ) : (
                  <th key={b.id} scope="col" className={cn(head, "text-right")}>
                    {b.name}
                  </th>
                ),
              )}
              {!showLocations && visible.length > 1 ? (
                <th scope="col" className={cn(head, "border-l border-[var(--color-border-subtle)] text-right")}>
                  All branches
                </th>
              ) : null}
            </tr>
          </thead>
          <tbody className="divide-y divide-[var(--color-border-subtle)]">
            {shown.length ? (
              shown.map((r) => (
                <tr key={r.productId} className="hover:bg-muted/40">
                  <th scope="row" className="sticky left-0 z-[1] bg-card px-3 py-2 text-left font-medium whitespace-nowrap">
                    {r.name}
                    {r.sub ? <span className="block text-xs font-normal text-muted-foreground">{r.sub}</span> : null}
                  </th>
                  {visible.map((b) => {
                    const q = branchQty(r, b, loc);
                    const st = cellStatus(q, loc ? 0 : r.reorderLevel);
                    const branchCell = (
                      <button
                        type="button"
                        onClick={() => onOpenCell?.(r, b)}
                        aria-label={`${r.name} at ${b.name}: ${q}${st !== "ok" ? `, ${st === "out" ? "out of stock" : st}` : ""}. Open details`}
                        className="w-full cursor-pointer rounded-sm px-1 text-right outline-none hover:underline focus-visible:ring-2 focus-visible:ring-ring"
                      >
                        <QtyCell q={q} status={st} strong={showLocations} negativeInside={!showLocations && b.locations.some((l) => (!loc || l.name === loc) && (r.qty[l.id] ?? 0) < 0)} />
                      </button>
                    );
                    return showLocations ? (
                      <React.Fragment key={b.id}>
                        {b.locations
                          .filter((l) => !loc || l.name === loc)
                          .map((l, i) => {
                            const lq = r.qty[l.id] ?? 0;
                            return (
                              <td key={l.id} className={cn("px-3 py-2 text-right", i === 0 && "border-l border-[var(--color-border-subtle)]")}>
                                <QtyCell q={lq} status={lq < 0 ? "negative" : "ok"} />
                              </td>
                            );
                          })}
                        <td className="px-2 py-2">{branchCell}</td>
                      </React.Fragment>
                    ) : (
                      <td key={b.id} className="px-2 py-2">
                        {branchCell}
                      </td>
                    );
                  })}
                  {visible.length > 1 ? (
                    <td className="border-l border-[var(--color-border-subtle)] px-3 py-2 text-right font-semibold tabular-nums">{n0.format(total(r))}</td>
                  ) : null}
                </tr>
              ))
            ) : (
              <tr>
                <td colSpan={999} className="px-3 py-8 text-center text-sm text-muted-foreground">
                  No products match these filters.
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </div>
    </div>
  );
}

/* ----------------------------------- Inventory log ----------------------------------- */

export type MovementAction = "Sale" | "Return" | "Received" | "Adjustment" | "Waste" | "Conversion" | "Issue" | "Transfer out" | "Transfer in" | "Stocktake";

export type Movement = {
  id: string;
  at: string;
  product: string;
  action: MovementAction;
  /** Signed change in units. */
  change: number;
  /** Quantity at that location after the movement. */
  newQty: number;
  branch: string;
  location: string;
  user: string;
  /** The document behind it: SALE-005200, GRN-2026-0187, TRF-0042. */
  reference: string;
};

/**
 * Inventory log (IN-11): every stock movement with its change, the new quantity, where, who, when and the
 * document behind it. Read-only; the change shows a sign and an arrow, not a colour, since a decrease
 * isn't bad (a sale) and an increase isn't good (a mistaken count).
 */
export function InventoryLog({ movements, formatAt }: { movements: Movement[]; formatAt: (iso: string) => string }) {
  const actions = [...new Set(movements.map((m) => m.action))];
  const [action, setAction] = React.useState<string>("");
  const shown = movements.filter((m) => !action || m.action === action);
  return (
    <div className="space-y-3">
      <div className="flex flex-wrap items-center gap-2">
        <NativeSelect aria-label="Movement type" value={action} onChange={(e) => setAction(e.target.value)}>
          <NativeSelectOption value="">All movements</NativeSelectOption>
          {actions.map((a) => (
            <NativeSelectOption key={a} value={a}>
              {a}
            </NativeSelectOption>
          ))}
        </NativeSelect>
        <span className="text-xs text-muted-foreground tabular-nums">
          {shown.length} {shown.length === 1 ? "movement" : "movements"}
        </span>
      </div>
      <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
        <table className="w-full border-collapse text-sm">
          <caption className="sr-only">Inventory log</caption>
          <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
            <tr>
              {["When", "Product", "Movement", "Change", "New quantity", "Where", "By", "Reference"].map((h) => (
                <th key={h} scope="col" className={cn("px-3 py-2 font-medium whitespace-nowrap", (h === "Change" || h === "New quantity") && "text-right")}>
                  {h}
                </th>
              ))}
            </tr>
          </thead>
          <tbody className="divide-y divide-[var(--color-border-subtle)]">
            {shown.map((m) => (
              <tr key={m.id}>
                <td className="px-3 py-2 whitespace-nowrap text-muted-foreground tabular-nums">{formatAt(m.at)}</td>
                <td className="px-3 py-2 font-medium">{m.product}</td>
                <td className="px-3 py-2 whitespace-nowrap">{m.action}</td>
                <td className="px-3 py-2 text-right whitespace-nowrap tabular-nums">
                  <span className="inline-flex items-center gap-1">
                    {m.change < 0 ? <ArrowDownRightIcon className="size-3.5 text-muted-foreground" aria-hidden /> : <ArrowUpRightIcon className="size-3.5 text-muted-foreground" aria-hidden />}
                    {m.change < 0 ? `−${n0.format(-m.change)}` : `+${n0.format(m.change)}`}
                  </span>
                </td>
                <td className={cn("px-3 py-2 text-right tabular-nums", m.newQty < 0 && "font-semibold text-status-error-text")}>{m.newQty < 0 ? `−${n0.format(-m.newQty)}` : n0.format(m.newQty)}</td>
                <td className="px-3 py-2 whitespace-nowrap">
                  {m.branch}
                  <span className="block text-xs text-muted-foreground">{m.location}</span>
                </td>
                <td className="px-3 py-2 whitespace-nowrap">{m.user}</td>
                <td className="px-3 py-2 font-mono text-xs whitespace-nowrap">{m.reference}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}
