"use client";

import * as React from "react";
import { EyeIcon, EyeOffIcon, TagIcon } from "lucide-react";

import { kes } from "@/components/medora/payroll";
import { promoDiscount, promoStatus, type Promo } from "@/components/medora/settings-ops";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { count } from "@/lib/format";

/*
 * The till basket in full (PS-02, PS-04, PS-07). Each line shows what the cashier must check before
 * handing goods over: batch and expiry being sold, unit, shelf, quantity, price, discount and total.
 * Running promotions apply by themselves and are named on the bill; cost and margin appear only for roles
 * allowed to see them, behind a switch, so they're not on screen when a customer is looking.
 */

export type BasketItem = { id: number; name: string; unit: string; qty: number; unitPrice: number; discount?: number; batch: string; expiry: string; shelf?: string; cost: number; category: string; taxCode: "A" | "B"; rx?: boolean };

const plural = (u: string) => (/(x|s|ch|sh)$/.test(u) ? `${u}es` : `${u}s`);
const r2 = (n: number) => Math.round(n * 100) / 100;
const lineGross = (i: BasketItem) => r2(i.qty * (i.unitPrice - (i.discount ?? 0)));

export type AppliedPromo = { name: string; amount: number; scope: Promo["scope"]; target?: string };
export type MissedPromo = { name: string; short: number };

/**
 * Which running promotions apply to this basket, and how much each takes off. Product and category
 * promotions apply to their lines first; a basket promotion then applies to what remains. Promotions that
 * missed only on the minimum basket are returned too, so the cashier can say "spend KES 120 more".
 */
export function applyPromos(items: BasketItem[], promos: Promo[], ctx: { branch: string; today: string; group?: string }) {
  const applied: AppliedPromo[] = [];
  const missed: MissedPromo[] = [];
  const running = promos.filter((p) => promoStatus(p, ctx.today) === "Running" && p.branches.includes(ctx.branch) && (!p.group || p.group === ctx.group));
  const basket = items.reduce((s, i) => s + lineGross(i), 0);
  let left = basket;
  for (const p of running.filter((p) => p.scope !== "Basket")) {
    const eligible = items.filter((i) => (p.scope === "Product" ? i.name === p.target : i.category === p.target)).reduce((s, i) => s + lineGross(i), 0);
    if (!eligible) continue;
    if (basket < p.minBasket) {
      missed.push({ name: p.name, short: r2(p.minBasket - basket) });
      continue;
    }
    const d = promoDiscount(p, basket, eligible);
    if (d > 0) {
      applied.push({ name: p.name, amount: d, scope: p.scope, target: p.target });
      left -= d;
    }
  }
  for (const p of running.filter((p) => p.scope === "Basket")) {
    if (basket < p.minBasket) {
      missed.push({ name: p.name, short: r2(p.minBasket - basket) });
      continue;
    }
    const d = Math.min(promoDiscount(p, left, left), left);
    if (d > 0) {
      applied.push({ name: p.name, amount: d, scope: p.scope });
      left -= d;
    }
  }
  return { basket: r2(basket), applied, missed };
}

/**
 * Totals with promotions spread over the lines they cover, so VAT is charged on what the customer
 * actually pays. VAT is exclusive (16% on code B, none on A), matching the receipt.
 */
export function basketTotals(items: BasketItem[], applied: AppliedPromo[]) {
  const share = new Map<number, number>(items.map((i) => [i.id, 0]));
  for (const a of applied) {
    const covered = items.filter((i) => a.scope === "Basket" || (a.scope === "Product" ? i.name === a.target : i.category === a.target));
    const base = covered.reduce((s, i) => s + lineGross(i), 0);
    for (const i of covered) share.set(i.id, (share.get(i.id) ?? 0) + (base ? (a.amount * lineGross(i)) / base : 0));
  }
  let net = 0;
  let vat = 0;
  for (const i of items) {
    const n = lineGross(i) - (share.get(i.id) ?? 0);
    net += n;
    if (i.taxCode === "B") vat += n * 0.16;
  }
  const lineDiscounts = items.reduce((s, i) => s + i.qty * (i.discount ?? 0), 0);
  return { list: r2(items.reduce((s, i) => s + i.qty * i.unitPrice, 0)), lineDiscounts: r2(lineDiscounts), promos: r2(applied.reduce((s, a) => s + a.amount, 0)), net: r2(net), vat: r2(vat), total: r2(net + vat), share };
}

function Margin({ item, promoShare }: { item: BasketItem; promoShare: number }) {
  const sell = (lineGross(item) - promoShare) / item.qty;
  const margin = sell ? ((sell - item.cost) / sell) * 100 : 0;
  const markup = item.cost ? ((sell - item.cost) / item.cost) * 100 : 0;
  const below = sell < item.cost;
  return (
    <span className={cn("block text-[11px] tabular-nums", below ? "font-medium text-status-error-text" : "text-muted-foreground")}>
      Cost {kes(item.cost)} · margin {margin.toFixed(1)}% · markup {markup.toFixed(1)}%{below ? " · below cost" : ""}
    </span>
  );
}

/** `onSaveQuote` (PS-09, AC-15): turns the basket into a quotation at today's prices and promotions, without taking payment or stock. */
export function TillBasket({ items, promos, branch, today, group, canSeeCost, onSaveQuote }: { items: BasketItem[]; promos: Promo[]; branch: string; today: string; group?: string; canSeeCost: boolean; onSaveQuote?: (q: { total: number; lines: number }) => void }) {
  const [showCost, setShowCost] = React.useState(false);
  const { applied, missed } = applyPromos(items, promos, { branch, today, group });
  const t = basketTotals(items, applied);
  const profit = r2(t.net - items.reduce((s, i) => s + i.qty * i.cost, 0));
  return (
    <div className="overflow-hidden rounded-lg border border-[var(--color-border-panel)] bg-card">
      <div className="flex items-center justify-between gap-2 border-b border-[var(--color-border-subtle)] px-3 py-2">
        <p className="text-sm font-medium">
          Basket · {count(items.length, "line")}
        </p>
        <span className="flex flex-wrap justify-end gap-1">
          {onSaveQuote ? (
            <Button size="sm" variant="outline" disabled={!items.length} onClick={() => onSaveQuote({ total: t.total, lines: items.length })}>
              Save as quotation
            </Button>
          ) : null}
          {canSeeCost ? (
            <Button size="sm" variant="ghost" aria-pressed={showCost} onClick={() => setShowCost((v) => !v)}>
              {showCost ? <EyeOffIcon data-icon="inline-start" /> : <EyeIcon data-icon="inline-start" />}
              {showCost ? "Hide cost and margin" : "Show cost and margin"}
            </Button>
          ) : null}
        </span>
      </div>
      <ul className="divide-y divide-[var(--color-border-subtle)]">
        {items.map((i) => (
          <li key={i.id} className="grid grid-cols-[1fr_auto] gap-x-3 gap-y-0.5 px-3 py-2 text-sm">
            <span className="min-w-0">
              <span className="font-medium">{i.name}</span>
              {i.rx ? <span className="ml-1.5 rounded bg-status-warning-bg px-1 text-[10px] font-semibold text-status-warning-text">Rx</span> : null}
              <span className="block text-xs text-muted-foreground tabular-nums">
                Batch <span className="font-mono">{i.batch}</span> · exp {i.expiry}
                {i.shelf ? ` · shelf ${i.shelf}` : ""}
              </span>
              <span className="block text-xs tabular-nums">
                {i.qty} {i.qty === 1 ? i.unit : plural(i.unit)} × {kes(i.unitPrice)}
                {i.discount ? <span className="text-status-success-text"> less {kes(i.discount)} each</span> : null}
                <span className="ml-1 text-muted-foreground">({i.taxCode === "B" ? "VAT 16%" : "Exempt"})</span>
              </span>
              {showCost ? <Margin item={i} promoShare={t.share.get(i.id) ?? 0} /> : null}
            </span>
            <span className="text-right font-medium tabular-nums">{kes(lineGross(i))}</span>
          </li>
        ))}
      </ul>
      <dl className="space-y-1 border-t border-[var(--color-border-panel)] px-3 py-2 text-sm tabular-nums">
        <div className="flex justify-between text-muted-foreground">
          <dt>Items at list price</dt>
          <dd>{kes(t.list)}</dd>
        </div>
        {t.lineDiscounts ? (
          <div className="flex justify-between text-status-success-text">
            <dt>Line discounts</dt>
            <dd>−{kes(t.lineDiscounts)}</dd>
          </div>
        ) : null}
        {applied.map((a) => (
          <div key={a.name} className="flex justify-between text-status-success-text">
            <dt className="flex items-center gap-1">
              <TagIcon className="size-3.5" aria-hidden />
              {a.name}
            </dt>
            <dd>−{kes(a.amount)}</dd>
          </div>
        ))}
        <div className="flex justify-between">
          <dt>VAT</dt>
          <dd>{kes(t.vat)}</dd>
        </div>
        <div className="flex justify-between border-t border-[var(--color-border-subtle)] pt-1 text-base font-semibold">
          <dt>Total</dt>
          <dd>{kes(t.total)}</dd>
        </div>
        {showCost ? (
          <div className={cn("flex justify-between text-xs", profit < 0 ? "text-status-error-text" : "text-muted-foreground")}>
            <dt>Gross profit on this sale (ex VAT)</dt>
            <dd>{kes(profit)}</dd>
          </div>
        ) : null}
      </dl>
      {missed.length ? (
        <ul className="border-t border-[var(--color-border-subtle)] bg-muted/40 px-3 py-2 text-xs">
          {missed.map((m) => (
            <li key={m.name}>
              <TagIcon className="mr-1 inline size-3.5 align-[-2px]" aria-hidden />
              Spend {kes(m.short)} more for <span className="font-medium">{m.name}</span>.
            </li>
          ))}
        </ul>
      ) : null}
    </div>
  );
}
