"use client";

import * as React from "react";
import { MinusIcon, PlusIcon } from "lucide-react";

import { Button } from "@/components/ui/button";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { cn } from "@/lib/utils";

/**
 * A way a product is sold. `size` is how many base units it holds (Tablet 1, Strip 10, Box 100);
 * `price` is set per pack, so a bigger pack can be cheaper per tablet. Stock is always counted in base units.
 */
export type SellUnit = { name: string; size: number; price: number };

export type UnitProduct = {
  name: string;
  /** The smallest unit stock is counted in: "tablet", "capsule", "ml". */
  baseUnit: string;
  /** Stock on hand, in base units. */
  stock: number;
  units: SellUnit[];
};

const plural = (n: number, word: string) =>
  `${n.toLocaleString("en-US")} ${word}${n === 1 ? "" : "s"}`;

/** How many base units `qty` of `unit` is, e.g. 2 strips of 10 = 20 tablets. */
export const toBaseUnits = (unit: SellUnit, qty: number) => unit.size * qty;

/** The most of this unit that stock can cover. */
export const availableOf = (p: UnitProduct, unit: SellUnit) =>
  Math.floor(p.stock / unit.size);

/** Per-base-unit price and the saving against the smallest unit's per-base price. */
function perBase(p: UnitProduct, unit: SellUnit) {
  const smallest = [...p.units].sort((a, b) => a.size - b.size)[0];
  const each = unit.price / unit.size;
  const ref = smallest.price / smallest.size;
  const saving = ref > 0 ? Math.round((1 - each / ref) * 100) : 0;
  return { each, saving: unit === smallest ? 0 : saving };
}

/* ------------------------------------ Unit picker ------------------------------------ */

/**
 * Choose how to sell a product: one card per unit with its price, contents, price per base unit (and the saving
 * on bigger packs) and how many stock covers. Units stock cannot cover are disabled, never hidden, so the
 * cashier sees why. Arrow keys move between units (radio group semantics).
 */
export function UnitPicker({
  product,
  value,
  onChange,
  disabled,
}: {
  product: UnitProduct;
  value: string;
  onChange: (unitName: string) => void;
  disabled?: boolean;
}) {
  const refs = React.useRef<(HTMLButtonElement | null)[]>([]);
  const units = product.units;

  const move = (from: number, dir: 1 | -1) => {
    for (let i = 1; i <= units.length; i++) {
      const j = (from + dir * i + units.length) % units.length;
      if (availableOf(product, units[j]) > 0) {
        onChange(units[j].name);
        refs.current[j]?.focus();
        return;
      }
    }
  };

  return (
    <div className="@container">
      <div
        role="radiogroup"
        aria-label={`Sell ${product.name} by`}
        className={cn(
          "grid gap-2",
          units.length >= 3 ? "@md:grid-cols-3" : "@xs:grid-cols-2",
        )}
      >
        {units.map((u, i) => {
          const avail = availableOf(product, u);
          const off = disabled || avail === 0;
          const on = value === u.name;
          const { each, saving } = perBase(product, u);
          return (
            <button
              key={u.name}
              ref={(el) => {
                refs.current[i] = el;
              }}
              type="button"
              role="radio"
              aria-checked={on}
              aria-disabled={off}
              tabIndex={on ? 0 : -1}
              onClick={() => !off && onChange(u.name)}
              onKeyDown={(e) => {
                const dir =
                  e.key === "ArrowRight" || e.key === "ArrowDown"
                    ? 1
                    : e.key === "ArrowLeft" || e.key === "ArrowUp"
                      ? -1
                      : 0;
                if (!dir) return;
                e.preventDefault();
                move(i, dir);
              }}
              className={cn(
                "flex flex-col items-start gap-0.5 rounded-lg border p-3 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring",
                on
                  ? "border-primary bg-accent"
                  : "border-border bg-card hover:border-[var(--color-border-strong)]",
                off
                  ? "cursor-not-allowed opacity-55 hover:border-border"
                  : "cursor-pointer",
              )}
            >
              <span className="flex w-full items-baseline justify-between gap-2">
                <span
                  className={cn(
                    "text-sm font-semibold",
                    on && "text-accent-foreground",
                  )}
                >
                  {u.name}
                </span>
                <span className="text-sm font-semibold tabular-nums">
                  {u.price.toFixed(2)}
                </span>
              </span>
              <span className="text-xs text-muted-foreground">
                {u.size === 1
                  ? `1 ${product.baseUnit}`
                  : plural(u.size, product.baseUnit)}
              </span>
              <span className="text-xs text-muted-foreground tabular-nums">
                {each.toFixed(2)} per {product.baseUnit}
                {saving > 0 ? (
                  <span className="ml-1 font-medium text-status-success-text">
                    save {saving}%
                  </span>
                ) : null}
              </span>
              <span
                className={cn(
                  "mt-1 text-xs tabular-nums",
                  avail === 0
                    ? "font-medium text-status-error-text"
                    : "text-muted-foreground",
                )}
              >
                {avail === 0
                  ? `Not enough stock (${plural(product.stock, product.baseUnit)} left)`
                  : `${avail.toLocaleString("en-US")} available`}
              </span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

/* ----------------------------------- Unit quantity ----------------------------------- */

/**
 * Compact quantity + unit for a cart line: − 2 + [Strip ▾] and "= 20 tablets". Changing the unit keeps the
 * quantity but clamps it to what stock covers in the new unit. The plus button stops at stock.
 */
export function UnitQuantity({
  product,
  qty,
  unit,
  onChange,
  disabled,
}: {
  product: UnitProduct;
  qty: number;
  unit: string;
  onChange: (next: { qty: number; unit: string }) => void;
  disabled?: boolean;
}) {
  const u = product.units.find((x) => x.name === unit) ?? product.units[0];
  const max = availableOf(product, u);
  const single = product.units.length === 1;
  return (
    <div className="flex flex-col items-end gap-1">
      <div className="flex items-center gap-1.5">
        <div className="inline-flex h-8 items-center rounded-lg border border-input bg-background">
          <Button
            variant="ghost"
            size="icon-xs"
            className="ml-0.5"
            aria-label={`Fewer ${u.name.toLowerCase()}s`}
            disabled={disabled || qty <= 1}
            onClick={() => onChange({ qty: qty - 1, unit })}
          >
            <MinusIcon />
          </Button>
          <span
            className="min-w-7 text-center text-sm font-semibold tabular-nums"
            aria-live="polite"
          >
            {qty}
          </span>
          <Button
            variant="ghost"
            size="icon-xs"
            className="mr-0.5"
            aria-label={`More ${u.name.toLowerCase()}s`}
            disabled={disabled || qty >= max}
            onClick={() => onChange({ qty: qty + 1, unit })}
          >
            <PlusIcon />
          </Button>
        </div>
        {single ? (
          <span className="text-sm text-muted-foreground">{u.name}</span>
        ) : (
          <Select
            value={unit}
            onValueChange={(v) => {
              const next = product.units.find((x) => x.name === v);
              if (next)
                onChange({
                  qty: Math.max(1, Math.min(qty, availableOf(product, next))),
                  unit: next.name,
                });
            }}
            disabled={disabled}
          >
            <SelectTrigger
              size="sm"
              className="w-auto min-w-24"
              aria-label={`Unit for ${product.name}`}
            >
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              {product.units.map((x) => {
                const a = availableOf(product, x);
                return (
                  <SelectItem key={x.name} value={x.name} disabled={a === 0}>
                    <span className="flex w-full items-baseline justify-between gap-3">
                      <span>
                        {x.name}
                        {x.size > 1 ? (
                          <span className="text-muted-foreground">
                            {" "}
                            ×{x.size}
                          </span>
                        ) : null}
                      </span>
                      <span className="text-xs text-muted-foreground tabular-nums">
                        {a === 0 ? "out" : x.price.toFixed(2)}
                      </span>
                    </span>
                  </SelectItem>
                );
              })}
            </SelectContent>
          </Select>
        )}
      </div>
      <span className="text-xs text-muted-foreground tabular-nums">
        = {plural(toBaseUnits(u, qty), product.baseUnit)}
        {qty >= max ? (
          <span className="ml-1 text-status-warning-text">· stock limit</span>
        ) : null}
      </span>
    </div>
  );
}
