// Reseller portal — Quotes. This is the ONLY place a Quote is ever
// created (see routes/portal.ts's POST /quotes) — the admin Quotes page
// (admin-quotes-page.jsx) is strictly read-only-plus-actions (Accept /
// Request PO / Reject), never a create surface. Reached via the "Quotes"
// tab in ResellerShell's top nav (see reseller-shell.jsx's
// RESELLER_NAV_ITEMS) — replaces the "Pricing schedules" ComingSoonCard
// placeholder that used to sit on the dashboard.
//
// Flow (confirmed with the customer):
//   1. Reseller picks products from their own priced catalogue
//      (GET /api/portal/products — priced by their company's tier +
//      default currency, same price grid the admin Price List page
//      edits) and quantities, chooses a tax treatment (Standard, with an
//      editable rate — Sales Tax varies by US state, VAT occasionally
//      changes — or Export - No Tax, which forces the rate to 0
//      regardless of what's typed), and submits (POST /api/portal/quotes).
//      unit_price is snapshotted server-side at submit time from the
//      price grid — never trusted from the client.
//   2. The quote then sits in "Submitted" until Solo staff act on it in
//      the admin Quotes page. The reseller can attach a PO number and/or
//      a PO PDF to their OWN quote AT ANY TIME regardless of status
//      (POST /api/portal/quotes/:id/po, multipart) — this is what
//      unlocks admin's Accept button; attaching a PO also auto-flips a
//      "PO Requested" quote back to "Submitted" so it reappears in
//      admin's queue.
//   3. Once accepted, the quote is converted into an Order by admin and
//      pushed to Xero/QuickBooks — nothing further to do here.
//
// VAT vs Sales Tax is purely a display-label choice by the company's
// region (see taxLabelFor below) — both are stored identically
// server-side as tax_treatment/tax_rate (see migrations/0025_quotes.sql).
//
// Reuses FieldBlock/DarkInput from reseller-login-page.jsx (already
// loaded earlier — see index.html; the component keeps its original
// name but its internal styling is being converted to the light-theme
// palette), matching ResellerShell's white theme now that the whole
// portal has flipped from black-on-white to white/black text on a
// white background (see reseller-shell.jsx's header comment for the
// full rationale).

// submitted/accepted/rejected keep their functional status colour (this
// is meaningful colour-coding a user scans a list by, not decoration).
// po_requested was previously a plain bright-white "action needed" flag
// against the old black background — on a white background that's
// invisible, so it's now a warm amber tone (matching admin's own
// po_requested colour in admin-quotes-page.jsx) instead.
const QUOTE_STATUS_LABELS_PORTAL = { submitted: "Submitted", po_requested: "PO Requested", accepted: "Accepted", rejected: "Rejected" };
const QUOTE_STATUS_COLORS_PORTAL = {
  submitted: "var(--pt-status-submitted)",
  po_requested: "var(--pt-status-po-requested)",
  accepted: "var(--pt-status-accepted)",
  rejected: "var(--pt-status-rejected)",
};

function portalMoney(amount, currency) {
  const symbols = { GBP: "£", USD: "$", EUR: "€" };
  return `${symbols[currency] || ""}${Number(amount).toFixed(2)}`;
}

// UK/EU -> VAT, North America/Rest of the World -> Sales Tax. Purely a
// label — the underlying tax_treatment/tax_rate fields are identical
// either way (see migrations/0025_quotes.sql's header comment).
function taxLabelFor(region) {
  return region === "UK" || region === "EU" ? "VAT" : "Sales Tax";
}

function ResellerQuotesPage({ onNavigate }) {
  const [state, setState] = useState({ status: "loading", user: null, company: null, isSoloStaff: false });
  const [companies, setCompanies] = useState([]);
  const [quotes, setQuotes] = useState({ status: "loading", items: [] });
  const [catalogue, setCatalogue] = useState({ status: "loading", products: [], defaultCurrency: "GBP" });
  const [openId, setOpenId] = useState(null);
  const [showNew, setShowNew] = useState(false);

  const loadQuotes = () => {
    setQuotes((q) => ({ ...q, status: "loading" }));
    fetch("/api/portal/quotes", { credentials: "same-origin" })
      .then((r) => (r.ok ? r.json() : { quotes: [] }))
      .then((data) => setQuotes({ status: "ready", items: data.quotes || [] }))
      .catch(() => setQuotes({ status: "ready", items: [] }));
  };

  const loadCatalogue = () => {
    fetch("/api/portal/products", { credentials: "same-origin" })
      .then((r) => (r.ok ? r.json() : { products: [], defaultCurrency: "GBP" }))
      .then((data) => setCatalogue({ status: "ready", products: data.products || [], defaultCurrency: data.defaultCurrency || "GBP" }))
      .catch(() => setCatalogue({ status: "ready", products: [], defaultCurrency: "GBP" }));
  };

  const loadMe = (onDone) => {
    fetch("/api/portal/me", { credentials: "same-origin" })
      .then(async (r) => {
        if (!r.ok) throw new Error("not signed in");
        return r.json();
      })
      .then((data) => {
        setState({ status: "ready", user: data.user, company: data.company, isSoloStaff: !!data.isSoloStaff });
        if (data.isSoloStaff) {
          fetch("/api/portal/companies", { credentials: "same-origin" })
            .then((r) => (r.ok ? r.json() : { companies: [] }))
            .then((d) => setCompanies(d.companies || []))
            .catch(() => setCompanies([]));
        }
        if (onDone) onDone();
      })
      .catch(() => onNavigate("reseller-login"));
  };

  const handleSwitchCompany = (companyId) => {
    fetch("/api/portal/switch-company", {
      method: "POST", credentials: "same-origin",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ companyId }),
    })
      .then((r) => (r.ok ? r.json() : Promise.reject()))
      .then(() => loadMe(() => { loadQuotes(); loadCatalogue(); }))
      .catch(() => { /* switch failed silently */ });
  };

  useEffect(() => {
    loadMe();
    loadQuotes();
    loadCatalogue();
  }, []); // eslint-disable-line react-hooks/exhaustive-deps -- mount-only

  if (state.status === "loading") {
    return <section style={{ background: "var(--pt-bg)", minHeight: "calc(100vh - 88px)" }} />;
  }

  const { user, company, isSoloStaff } = state;
  const taxLabel = taxLabelFor(company && company.region);

  return (
    <ResellerShell
      page="portal-quotes" onNavigate={onNavigate}
      userName={user.name} companyName={company.name}
      isSoloStaff={isSoloStaff} companies={companies}
      currentCompanyId={company.id} onSwitchCompany={handleSwitchCompany}
      subtitle="Reseller portal"
      title="Quotes."
      actions={
        <button
          type="button" data-testid="portal-new-quote-toggle"
          onClick={() => setShowNew((v) => !v)}
          style={{
            background: showNew ? "var(--pt-surface-strong)" : "var(--pt-accent-bg)",
            color: showNew ? "var(--pt-fg)" : "var(--pt-accent-fg)",
            border: `1px solid ${showNew ? "var(--pt-border-strong)" : "var(--pt-accent-bg)"}`,
            padding: "12px 20px", cursor: "pointer",
            fontFamily: "var(--font-body)", fontSize: 12, fontWeight: 500,
            letterSpacing: "0.14em", textTransform: "uppercase",
          }}
        >{showNew ? "Cancel" : "+ New quote"}</button>
      }
    >
      <p style={{
        fontFamily: "var(--font-body)", fontSize: 14,
        color: "var(--pt-fg-dim)", margin: "-16px 0 36px",
      }}>
        Build a quote from your priced catalogue below, submit it to Solo, then attach a PO once you have one — Solo staff will accept it and push it to your order pipeline.
      </p>

      {showNew && (
        <NewQuoteForm
          catalogue={catalogue}
          taxLabel={taxLabel}
          onCreated={() => { setShowNew(false); loadQuotes(); }}
        />
      )}

      <div style={{
        fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.28em",
        textTransform: "uppercase", color: "var(--pt-fg-dimmer)", fontWeight: 500,
        marginBottom: 16, marginTop: showNew ? 36 : 0,
      }}>
        Your quotes {quotes.status === "ready" && `(${quotes.items.length})`}
      </div>

      {quotes.status === "loading" ? (
        <div style={{ fontFamily: "var(--font-body)", fontSize: 13.5, color: "var(--pt-fg-faint2)" }}>Loading…</div>
      ) : quotes.items.length === 0 ? (
        <div style={{
          background: "var(--pt-surface)", border: "1px solid var(--pt-border)",
          padding: "22px 24px", fontFamily: "var(--font-body)", fontSize: 13.5, color: "var(--pt-fg-dimmer)",
        }}>
          No quotes yet — build your first one above.
        </div>
      ) : (
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          {quotes.items.map((q) => (
            <PortalQuoteRow
              key={q.id} quote={q}
              open={openId === q.id}
              onToggle={() => setOpenId(openId === q.id ? null : q.id)}
              onChanged={loadQuotes}
              taxLabel={taxLabel}
            />
          ))}
        </div>
      )}
    </ResellerShell>
  );
}

// ─────────────────────────── New Quote builder ───────────────────────────

function NewQuoteForm({ catalogue, taxLabel, onCreated }) {
  const currency = catalogue.defaultCurrency || "GBP";
  const [lines, setLines] = useState([{ productId: "", quantity: 1 }]);
  const [taxTreatment, setTaxTreatment] = useState("standard");
  const [taxRate, setTaxRate] = useState("");
  const [poNumber, setPoNumber] = useState("");
  const [notes, setNotes] = useState("");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");

  const priceFor = (productId) => {
    const product = catalogue.products.find((p) => String(p.id) === String(productId));
    if (!product) return null;
    const entry = product.prices.find((pr) => pr.currency === currency);
    return entry ? entry.unitPrice : null;
  };

  const updateLine = (idx, patch) => {
    setLines((prev) => prev.map((l, i) => (i === idx ? { ...l, ...patch } : l)));
  };
  const addLine = () => setLines((prev) => [...prev, { productId: "", quantity: 1 }]);
  const removeLine = (idx) => setLines((prev) => (prev.length === 1 ? prev : prev.filter((_, i) => i !== idx)));

  const validLines = lines.filter((l) => l.productId && Number(l.quantity) > 0);
  const subtotal = validLines.reduce((sum, l) => {
    const price = priceFor(l.productId);
    return price == null ? sum : sum + price * Number(l.quantity);
  }, 0);
  const effectiveTaxRate = taxTreatment === "export_no_tax" ? 0 : (Number(taxRate) || 0);
  const taxAmount = subtotal * (effectiveTaxRate / 100);
  const total = subtotal + taxAmount;

  const hasMissingPrice = validLines.some((l) => priceFor(l.productId) == null);
  const canSubmit = validLines.length > 0 && !hasMissingPrice
    && (taxTreatment === "export_no_tax" || (taxRate !== "" && Number(taxRate) >= 0 && Number(taxRate) <= 100))
    && !busy;

  const submit = async () => {
    if (!canSubmit) return;
    setBusy(true); setError("");
    try {
      const res = await fetch("/api/portal/quotes", {
        method: "POST", credentials: "same-origin",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          currency,
          taxTreatment,
          taxRate: taxTreatment === "export_no_tax" ? 0 : Number(taxRate),
          notes: notes.trim() || undefined,
          poNumber: poNumber.trim() || undefined,
          items: validLines.map((l) => ({ productId: Number(l.productId), quantity: Number(l.quantity) })),
        }),
      });
      const d = await res.json().catch(() => ({}));
      if (!res.ok) { setError(d.error || "Something went wrong."); setBusy(false); return; }
      onCreated();
    } catch {
      setError("Couldn't reach the server. Check your connection and try again.");
      setBusy(false);
    }
  };

  return (
    <div style={{
      background: "var(--pt-surface)", border: "1px solid var(--pt-border-3)",
      padding: "28px 30px", marginBottom: 36,
    }}>
      <div style={{
        fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.28em",
        textTransform: "uppercase", color: "var(--pt-fg-dim)", fontWeight: 500, marginBottom: 20,
      }}>New quote — priced in {currency}</div>

      {error && (
        <div style={{
          background: "var(--pt-error-bg)", border: "1px solid var(--pt-error-border)",
          color: "var(--pt-error-text)", padding: "12px 14px", marginBottom: 18,
          fontFamily: "var(--font-body)", fontSize: 13, lineHeight: 1.5,
        }}>{error}</div>
      )}

      {/* Line items — Item, QTY, Item Price -> Total Price */}
      <div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 16 }}>
        {lines.map((line, idx) => {
          const price = priceFor(line.productId);
          const lineTotal = price != null ? price * Number(line.quantity || 0) : null;
          return (
            <div key={idx} style={{ display: "flex", gap: 10, alignItems: "flex-end", flexWrap: "wrap" }}>
              <div style={{ flex: "2 1 220px" }}>
                <FieldBlock label={idx === 0 ? "Item" : ""}>
                  <select
                    value={line.productId} onChange={(e) => updateLine(idx, { productId: e.target.value })}
                    data-testid={`quote-line-product-${idx}`}
                    style={{
                      width: "100%", boxSizing: "border-box", background: "var(--pt-surface-2)",
                      border: "1px solid var(--pt-border-strong)", color: "var(--pt-fg)",
                      fontFamily: "var(--font-body)", fontSize: 14, padding: "14px 16px", outline: "none",
                    }}
                  >
                    <option value="">Select a product…</option>
                    {catalogue.products.map((p) => {
                      const entry = p.prices.find((pr) => pr.currency === currency);
                      return (
                        <option key={p.id} value={p.id} disabled={!entry}>
                          {p.name}{entry ? ` — ${portalMoney(entry.unitPrice, currency)}` : " — no price set"}
                        </option>
                      );
                    })}
                  </select>
                </FieldBlock>
              </div>
              <div style={{ flex: "0 1 100px" }}>
                <FieldBlock label={idx === 0 ? "Qty" : ""}>
                  <DarkInput
                    type="number" min="1" value={line.quantity}
                    data-testid={`quote-line-qty-${idx}`}
                    onChange={(e) => updateLine(idx, { quantity: e.target.value })}
                  />
                </FieldBlock>
              </div>
              <div style={{ flex: "0 1 140px", paddingBottom: 22, fontFamily: "var(--font-body)", fontSize: 14, color: "var(--pt-fg-3)" }}>
                {lineTotal != null ? portalMoney(lineTotal, currency) : <span style={{ color: "var(--pt-status-rejected)" }}>No price</span>}
              </div>
              <button
                type="button" onClick={() => removeLine(idx)} disabled={lines.length === 1}
                data-testid={`quote-line-remove-${idx}`}
                style={{
                  background: "none", border: "none", cursor: lines.length === 1 ? "not-allowed" : "pointer",
                  color: "var(--pt-fg-faint2)", paddingBottom: 22, fontSize: 13,
                  fontFamily: "var(--font-body)", opacity: lines.length === 1 ? 0.4 : 1,
                }}
              >✕</button>
            </div>
          );
        })}
      </div>
      <button
        type="button" onClick={addLine} data-testid="quote-add-line"
        style={{
          background: "none", border: "1px solid var(--pt-border-strong)", color: "var(--pt-fg-4)",
          cursor: "pointer", padding: "8px 14px", marginBottom: 24,
          fontFamily: "var(--font-body)", fontSize: 11, fontWeight: 500, letterSpacing: "0.1em", textTransform: "uppercase",
        }}
      >+ Add item</button>

      {/* Tax treatment */}
      <div style={{ display: "flex", gap: 16, flexWrap: "wrap", marginBottom: 8 }}>
        <div style={{ flex: "1 1 220px" }}>
          <FieldBlock label="Tax treatment">
            <select
              value={taxTreatment} onChange={(e) => setTaxTreatment(e.target.value)}
              data-testid="quote-tax-treatment"
              style={{
                width: "100%", boxSizing: "border-box", background: "var(--pt-surface-2)",
                border: "1px solid var(--pt-border-strong)", color: "var(--pt-fg)",
                fontFamily: "var(--font-body)", fontSize: 14, padding: "14px 16px", outline: "none",
              }}
            >
              <option value="standard">Standard ({taxLabel})</option>
              <option value="export_no_tax">Export — No Tax</option>
            </select>
          </FieldBlock>
        </div>
        <div style={{ flex: "1 1 160px" }}>
          <FieldBlock label={`${taxLabel} rate (%)`} hint={taxTreatment === "export_no_tax" ? "Forced to 0% for exports." : undefined}>
            <DarkInput
              type="number" min="0" max="100" step="0.1"
              data-testid="quote-tax-rate"
              value={taxTreatment === "export_no_tax" ? 0 : taxRate}
              disabled={taxTreatment === "export_no_tax"}
              onChange={(e) => setTaxRate(e.target.value)}
            />
          </FieldBlock>
        </div>
      </div>

      {/* PO number (optional at creation — file can be attached after) */}
      <FieldBlock label="PO number (optional — you can add this or a PDF later)">
        <DarkInput value={poNumber} data-testid="quote-po-number-input" onChange={(e) => setPoNumber(e.target.value)} placeholder="e.g. PO-48291" />
      </FieldBlock>

      <FieldBlock label="Notes (optional)">
        <textarea
          value={notes} onChange={(e) => setNotes(e.target.value)} rows={2}
          data-testid="quote-notes-input"
          style={{
            width: "100%", boxSizing: "border-box", background: "var(--pt-surface-2)",
            border: "1px solid var(--pt-border-strong)", color: "var(--pt-fg)",
            fontFamily: "var(--font-body)", fontSize: 14, padding: "14px 16px", outline: "none", resize: "vertical",
          }}
        />
      </FieldBlock>

      {hasMissingPrice && (
        <div style={{ fontFamily: "var(--font-body)", fontSize: 12.5, color: "var(--pt-warn-text)", marginBottom: 14 }}>
          One or more selected items have no {currency} price on your tier — contact your account manager, or remove that line.
        </div>
      )}

      <div style={{
        display: "flex", justifyContent: "space-between", alignItems: "center",
        flexWrap: "wrap", gap: 16, borderTop: "1px solid var(--pt-border-2)",
        paddingTop: 18, marginTop: 6,
      }}>
        <div style={{ fontFamily: "var(--font-body)", fontSize: 14, color: "var(--pt-fg-2)" }}>
          Subtotal {portalMoney(subtotal, currency)}
          {taxAmount > 0 && <> + {taxLabel} {portalMoney(taxAmount, currency)}</>}
          {" "}→ Total <strong>{portalMoney(total, currency)}</strong>
        </div>
        <button
          type="button" onClick={submit} disabled={!canSubmit} data-testid="quote-submit"
          style={{
            background: canSubmit ? "var(--pt-accent-bg)" : "var(--pt-border-2)",
            color: canSubmit ? "var(--pt-accent-fg)" : "var(--pt-accent-fg-disabled)",
            border: `1px solid ${canSubmit ? "var(--pt-accent-bg)" : "var(--pt-border-2)"}`,
            padding: "14px 26px", cursor: canSubmit ? "pointer" : "not-allowed",
            fontFamily: "var(--font-body)", fontSize: 12.5, fontWeight: 500,
            letterSpacing: "0.16em", textTransform: "uppercase",
          }}
        >{busy ? "Submitting…" : "Submit quote →"}</button>
      </div>
    </div>
  );
}

// ─────────────────────────── Quote list + detail ───────────────────────────

function PortalQuoteRow({ quote, open, onToggle, onChanged, taxLabel }) {
  return (
    <div data-testid={`portal-quote-row-${quote.id}`} style={{
      background: "var(--pt-surface)", border: "1px solid var(--pt-border-2)",
    }}>
      <button
        type="button" onClick={onToggle} data-testid={`portal-quote-toggle-${quote.id}`}
        style={{
          width: "100%", background: "none", border: "none", cursor: "pointer",
          padding: "16px 20px", display: "flex", justifyContent: "space-between",
          alignItems: "center", flexWrap: "wrap", gap: 12, textAlign: "left",
        }}>
        <div>
          <div style={{ fontFamily: "var(--font-display)", fontWeight: 700, fontSize: 15, textTransform: "uppercase", color: "var(--pt-fg)" }}>
            {quote.quote_number}
          </div>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 12, color: "var(--pt-fg-dim)", marginTop: 4 }}>
            <span style={{ color: QUOTE_STATUS_COLORS_PORTAL[quote.status] || "var(--pt-fg-dim)", textTransform: "uppercase", letterSpacing: "0.08em", fontSize: 11 }}>
              {QUOTE_STATUS_LABELS_PORTAL[quote.status] || quote.status}
            </span>
            {" · "}{portalMoney(quote.subtotal, quote.currency)}
            {quote.po_number && <> · PO {quote.po_number}</>}
          </div>
        </div>
        <span style={{ fontFamily: "var(--font-body)", fontSize: 11, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--pt-fg-dimmer)" }}>
          {open ? "Close ↑" : "View →"}
        </span>
      </button>
      {open && <PortalQuoteDetail quoteId={quote.id} onChanged={onChanged} taxLabel={taxLabel} />}
    </div>
  );
}

function PortalQuoteDetail({ quoteId, onChanged, taxLabel }) {
  const [data, setData] = useState(null);
  const [error, setError] = useState("");
  const [poNumberInput, setPoNumberInput] = useState("");
  const [poFile, setPoFile] = useState(null);
  const [busy, setBusy] = useState(false);
  const [poSuccess, setPoSuccess] = useState("");

  const load = () => {
    fetch(`/api/portal/quotes/${quoteId}`, { credentials: "same-origin" })
      .then((r) => (r.ok ? r.json() : Promise.reject()))
      .then((d) => { setData(d); setPoNumberInput(d.quote.po_number || ""); })
      .catch(() => setError("Couldn't load this quote."));
  };

  useEffect(load, [quoteId]); // eslint-disable-line react-hooks/exhaustive-deps

  if (!data) {
    return (
      <div style={{ borderTop: "1px solid var(--pt-border-2)", padding: "20px", fontFamily: "var(--font-body)", fontSize: 13, color: "var(--pt-fg-dimmer)" }}>
        {error || "Loading…"}
      </div>
    );
  }

  const { quote, items } = data;
  const subtotal = items.reduce((sum, it) => sum + it.unit_price * it.quantity, 0);
  const taxAmount = quote.tax_treatment === "export_no_tax" ? 0 : subtotal * (quote.tax_rate / 100);
  const total = subtotal + taxAmount;
  const canAttachPo = quote.status !== "accepted" && quote.status !== "rejected";

  const submitPo = async () => {
    setBusy(true); setError(""); setPoSuccess("");
    try {
      const form = new FormData();
      if (poNumberInput.trim() !== (quote.po_number || "")) form.append("poNumber", poNumberInput.trim());
      if (poFile) form.append("file", poFile);
      if (!form.has("poNumber") && !form.has("file")) {
        setError("Enter a PO number and/or choose a PDF to attach.");
        setBusy(false);
        return;
      }
      const res = await fetch(`/api/portal/quotes/${quoteId}/po`, { method: "POST", credentials: "same-origin", body: form });
      const d = await res.json().catch(() => ({}));
      if (!res.ok) { setError(d.error || "Something went wrong."); setBusy(false); return; }
      setPoFile(null);
      setPoSuccess("PO saved.");
      load(); onChanged();
    } catch {
      setError("Couldn't reach the server. Check your connection and try again.");
    } finally { setBusy(false); }
  };

  return (
    <div style={{ borderTop: "1px solid var(--pt-border-2)", padding: "20px" }}>
      {error && (
        <div style={{
          background: "var(--pt-error-bg)", border: "1px solid var(--pt-error-border)",
          color: "var(--pt-error-text)", padding: "12px 14px", marginBottom: 16,
          fontFamily: "var(--font-body)", fontSize: 13, lineHeight: 1.5,
        }}>{error}</div>
      )}

      <div style={{ fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.24em", textTransform: "uppercase", color: "var(--pt-fg-dim)", fontWeight: 500, marginBottom: 12 }}>
        Line items ({items.length})
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 8, marginBottom: 18 }}>
        {items.map((it) => (
          <div key={it.id} style={{
            display: "flex", justifyContent: "space-between", alignItems: "center", gap: 10, flexWrap: "wrap",
            padding: "8px 12px", background: "var(--pt-surface)", border: "1px solid var(--pt-border)",
          }}>
            <div style={{ fontFamily: "var(--font-body)", fontSize: 13, color: "var(--pt-fg-3)" }}>
              {it.product_name} × {it.quantity} @ {portalMoney(it.unit_price, quote.currency)}
            </div>
            <div style={{ fontFamily: "var(--font-body)", fontSize: 13, color: "var(--pt-fg-3)", fontWeight: 600 }}>
              {portalMoney(it.unit_price * it.quantity, quote.currency)}
            </div>
          </div>
        ))}
      </div>

      {quote.notes && (
        <div style={{ marginBottom: 16 }}>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--pt-fg-faint2)", marginBottom: 4 }}>Notes</div>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 13.5, color: "var(--pt-fg-4)", whiteSpace: "pre-wrap" }}>{quote.notes}</div>
        </div>
      )}
      {quote.status === "po_requested" && quote.po_requested_note && (
        <div style={{ marginBottom: 16, padding: "12px 14px", background: "var(--pt-surface-3)", border: "1px solid var(--pt-border-strong)" }}>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--pt-fg)", fontWeight: 700, marginBottom: 4, display: "flex", alignItems: "center", gap: 6 }}><IconPulse size={6} />Solo asked for a PO</div>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 13.5, color: "var(--pt-fg-3)", whiteSpace: "pre-wrap" }}>{quote.po_requested_note}</div>
        </div>
      )}
      {quote.status === "rejected" && quote.rejected_reason && (
        <div style={{ marginBottom: 16, padding: "12px 14px", background: "var(--pt-status-rejected-bg)", border: "1px solid var(--pt-status-rejected-border)" }}>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--pt-status-rejected)", marginBottom: 4 }}>Rejected</div>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 13.5, color: "var(--pt-fg-3)", whiteSpace: "pre-wrap" }}>{quote.rejected_reason}</div>
        </div>
      )}

      <div style={{ fontFamily: "var(--font-body)", fontSize: 13.5, color: "var(--pt-fg-2)", marginBottom: 22 }}>
        Subtotal {portalMoney(subtotal, quote.currency)}
        {taxAmount > 0 && <> + {taxLabel} {portalMoney(taxAmount, quote.currency)}</>}
        {" "}→ Total <strong>{portalMoney(total, quote.currency)}</strong>
      </div>

      {quote.po_file_key && (
        <div style={{ marginBottom: 18 }}>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.14em", textTransform: "uppercase", color: "var(--pt-fg-faint2)", marginBottom: 4 }}>Current PO file</div>
          <a
            href={`/api/portal/quotes/${quoteId}/po/file`} target="_blank" rel="noreferrer"
            data-testid={`portal-quote-po-file-link-${quoteId}`}
            style={{ fontFamily: "var(--font-body)", fontSize: 13.5, color: "var(--pt-link)", textDecoration: "underline" }}
          >{quote.po_file_filename || "Download PDF"} →</a>
        </div>
      )}

      {canAttachPo ? (
        <div style={{ borderTop: "1px solid var(--pt-border-2)", paddingTop: 18 }}>
          <div style={{ fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.24em", textTransform: "uppercase", color: "var(--pt-fg-dim)", fontWeight: 500, marginBottom: 14 }}>
            Attach / update PO
          </div>
          {poSuccess && (
            <div style={{ fontFamily: "var(--font-body)", fontSize: 12.5, color: "var(--pt-success-text)", marginBottom: 12 }}>{poSuccess}</div>
          )}
          <div style={{ display: "flex", gap: 14, flexWrap: "wrap", alignItems: "flex-end" }}>
            <div style={{ flex: "1 1 220px" }}>
              <FieldBlock label="PO number">
                <DarkInput
                  value={poNumberInput} data-testid={`portal-quote-po-number-${quoteId}`}
                  onChange={(e) => setPoNumberInput(e.target.value)}
                />
              </FieldBlock>
            </div>
            <div style={{ flex: "1 1 220px", marginBottom: 22 }}>
              <div style={{
                fontFamily: "var(--font-body)", fontSize: 10, letterSpacing: "0.28em",
                textTransform: "uppercase", color: "var(--pt-fg-dim)", fontWeight: 500, marginBottom: 10,
              }}>PO PDF (optional)</div>
              <input
                type="file" accept="application/pdf"
                data-testid={`portal-quote-po-file-${quoteId}`}
                onChange={(e) => setPoFile(e.target.files && e.target.files[0] ? e.target.files[0] : null)}
                style={{ color: "var(--pt-fg-3)", fontFamily: "var(--font-body)", fontSize: 12.5 }}
              />
            </div>
            <button
              type="button" onClick={submitPo} disabled={busy}
              data-testid={`portal-quote-po-submit-${quoteId}`}
              style={{
                background: "var(--pt-accent-bg)", color: "var(--pt-accent-fg)", border: "1px solid var(--pt-accent-bg)",
                padding: "14px 22px", marginBottom: 22, cursor: busy ? "not-allowed" : "pointer",
                opacity: busy ? 0.6 : 1,
                fontFamily: "var(--font-body)", fontSize: 12, fontWeight: 500,
                letterSpacing: "0.14em", textTransform: "uppercase",
              }}
            >{busy ? "Saving…" : "Save PO"}</button>
          </div>
        </div>
      ) : (
        <div style={{ fontFamily: "var(--font-body)", fontSize: 12.5, color: "var(--pt-fg-faint2)" }}>
          This quote has been {quote.status} — no further changes can be made.
        </div>
      )}
    </div>
  );
}

Object.assign(window, { ResellerQuotesPage });
