// Nova Configurator main shell

const uid = () => Math.random().toString(36).slice(2, 9);

// Expand a catalog id into placed item(s). Stands become a group of smaller
// fixtures (a "kit"); everything else is a single item. All items from one add
// share a `gid` so they move, price, and remove as one unit.
const makeGroup = (cid, baseX, baseY) => {
  const meta = window.CATALOG.find((c) => c.id === cid);
  if (!meta) return [];
  const gid = uid();
  if (meta.kit) {
    return meta.kit.map((part) => ({
      uid: uid(), gid, cid,
      x: baseX + part.dx, y: baseY + part.dy,
      w: part.w, d: part.d, h: part.h, color: part.color, part: part.part,
    }));
  }
  return [{ uid: uid(), gid, cid, x: baseX, y: baseY, w: meta.w, d: meta.d, h: meta.h, color: meta.color }];
};

const Configurator = () => {
  // Active region (§12): currency, prices, freight + UK collect option. Set via
  // the nav switcher; a change reloads the page so this reads the new value.
  const region = window.getNovaRegion ? window.getNovaRegion() : "us";
  const R = window.NovaRegion ? window.NovaRegion.getRegion(region) : null;
  const fmt = (n) =>
    window.fmtMoney ? window.fmtMoney(n, region) : window.fmtUSD(n);
  const priceOf = (meta) =>
    window.NovaRegion ? window.NovaRegion.regionPrice(meta, region) : meta.price;

  const [space, setSpace] = React.useState({ w: 20, d: 20, mode: "indoor" });
  const [line, setLine] = React.useState("Eden");
  const [sub, setSub] = React.useState("oak");
  const [view, setView] = React.useState("iso");
  // Ship everywhere; UK can also collect from the workshop (free freight).
  const [fulfillment, setFulfillment] = React.useState("ship");
  const [items, setItems] = React.useState(() => makeGroup("stand-2020", 0, 0));
  const [selectedId, setSelectedId] = React.useState(null); // holds a gid
  const [filter, setFilter] = React.useState("All");
  const [financeMonths, setFinanceMonths] = React.useState(36);
  const [checkingOut, setCheckingOut] = React.useState(false);
  const [checkoutErr, setCheckoutErr] = React.useState(null);
  // Surface the result of a returned Stripe Checkout (?checkout=success|cancel)
  const [checkoutNotice, setCheckoutNotice] = React.useState(null);
  React.useEffect(() => {
    const p = new URLSearchParams(window.location.search).get("checkout");
    if (p === "success" || p === "cancel") setCheckoutNotice(p);
    if (p === "success" && window.novaTrack) window.novaTrack("checkout_complete");
  }, []);

  const addItem = (cid) => {
    const meta = window.CATALOG.find((c) => c.id === cid);
    if (!meta) return;
    const groupCount = new Set(items.map((i) => i.gid)).size;
    const bx = Math.max(0, Math.min(space.w - meta.w, (groupCount * 2) % Math.max(1, space.w - meta.w)));
    const by = Math.max(0, Math.min(space.d - meta.d, (groupCount * 2) % Math.max(1, space.d - meta.d)));
    const grp = makeGroup(cid, bx, by);
    setItems([...items, ...grp]);
    setSelectedId(grp[0].gid);
  };

  // Move every item in a group to its new position
  const moveGroupTo = (targets) => {
    const m = new Map(targets.map((t) => [t.uid, t]));
    setItems((prev) => prev.map((it) => (m.has(it.uid) ? { ...it, x: m.get(it.uid).x, y: m.get(it.uid).y } : it)));
  };

  const removeGroup = (gid) => {
    setItems((prev) => prev.filter((it) => it.gid !== gid));
    if (selectedId === gid) setSelectedId(null);
  };

  // Press Delete / Backspace to remove the selected item from the scene + quote
  // (ignored while typing in a form field)
  React.useEffect(() => {
    const onKey = (e) => {
      if (e.key !== "Delete" && e.key !== "Backspace") return;
      if (!selectedId) return;
      const t = e.target;
      const tag = t && t.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || (t && t.isContentEditable)) return;
      e.preventDefault();
      removeGroup(selectedId);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [selectedId]);

  // Material multiplier applied to fixtures + vault finishes
  const subMeta = window.MATERIAL_LINES[line].subs.find((s) => s.k === sub) || window.MATERIAL_LINES[line].subs[0];
  const mult = subMeta ? subMeta.mult : 1;

  // Quote: group by catalog id, counting distinct groups (gids)
  const grouped = React.useMemo(() => {
    const order = [];
    const map = new Map();
    items.forEach((it) => {
      if (!map.has(it.cid)) {
        map.set(it.cid, { meta: window.CATALOG.find((c) => c.id === it.cid), gids: [] });
        order.push(it.cid);
      }
      const g = map.get(it.cid);
      if (!g.gids.includes(it.gid)) g.gids.push(it.gid);
    });
    return order.map((cid) => {
      const g = map.get(cid);
      return { meta: g.meta, count: g.gids.length, gids: g.gids };
    });
  }, [items]);

  // Price through the shared quote engine so the live numbers match exactly
  // what the checkout function will charge. Values come back in integer cents.
  const checkoutLines = grouped.map((g) => ({ cid: g.meta.id, qty: g.count }));
  const quote = window.computeQuote({ lines: checkoutLines, materialLine: line, finish: sub, space, region, fulfillment });
  const subtotal = quote.subtotalCents / 100;
  const outdoorUpcharge = quote.outdoorCents / 100;
  const freight = quote.freightCents / 100;
  const install = quote.installCents / 100;
  const total = quote.totalCents / 100;

  // Over-capacity: sum each group's bounding-box area vs booth area + slack
  const overCap = (() => {
    const groups = new Map();
    items.forEach((it) => {
      if (!groups.has(it.gid)) groups.set(it.gid, { minX: it.x, minY: it.y, maxX: it.x + it.w, maxY: it.y + it.d });
      const g = groups.get(it.gid);
      g.minX = Math.min(g.minX, it.x); g.minY = Math.min(g.minY, it.y);
      g.maxX = Math.max(g.maxX, it.x + it.w); g.maxY = Math.max(g.maxY, it.y + it.d);
    });
    let area = 0;
    groups.forEach((g) => { area += (g.maxX - g.minX) * (g.maxY - g.minY); });
    return area > space.w * space.d * 1.15;
  })();

  const anyOutOfBounds = items.some(
    (it) => it.x + it.w > space.w + 0.01 || it.y + it.d > space.d + 0.01
  );

  // Financing calc (APR 7.9%)
  const apr = 0.079;
  const r = apr / 12;
  const n = financeMonths;
  const monthly = total > 0 ? (total * r) / (1 - Math.pow(1 + r, -n)) : 0;

  // Hand off to Stripe: POST the current configuration, let the server recompute
  // the authoritative price, then redirect to the hosted Checkout (card + ACH).
  const handleCheckout = async () => {
    if (checkingOut || checkoutLines.length === 0) return;
    setCheckingOut(true);
    setCheckoutErr(null);
    try {
      const res = await fetch("/api/checkout", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ lines: checkoutLines, materialLine: line, finish: sub, space, region, fulfillment }),
      });
      if (!res.ok) throw new Error(`Checkout failed (${res.status})`);
      const data = await res.json();
      if (!data || !data.url) throw new Error("No checkout URL returned");
      if (window.novaTrack)
        window.novaTrack("begin_checkout", {
          currency: quote.currency || "usd",
          value: quote.totalCents / 100,
          items: checkoutLines.map((l) => ({ item_id: l.cid, quantity: l.qty })),
        });
      window.location.assign(data.url);
    } catch (e) {
      setCheckoutErr((e && e.message) || "Something went wrong starting checkout.");
      setCheckingOut(false);
    }
  };

  // The configurator only shows items flagged `configurable` (priced + 3D-modeled
  // today). PODs, Vaults, Kits, accessories and unpriced fixtures stay in the data
  // model (shared/catalog.js) but are hidden until priced + modeled.
  const filters = ["All", "Fixtures", "Stands"];
  const visibleCatalog = window.CATALOG.filter(
    (c) => c.configurable && (filter === "All" || c.cat === filter)
  );

  return (
    <div className="nv-app">
      <Nav />
      <section className="nv-cfg">
        {/* LEFT — catalog + space + material */}
        <aside className="nv-cfg__pane">
          <div className="nv-cfg__h">01 — Space</div>
          <div className="nv-cfg__row">
            <label style={{ flex: 1 }}>
              <div className="nv-cfg__mini">Width (ft)</div>
              <input
                type="number" min={8} max={60} step={2}
                value={space.w}
                onChange={(e) => setSpace({ ...space, w: +e.target.value || 10 })}
              />
            </label>
            <label style={{ flex: 1 }}>
              <div className="nv-cfg__mini">Depth (ft)</div>
              <input
                type="number" min={8} max={60} step={2}
                value={space.d}
                onChange={(e) => setSpace({ ...space, d: +e.target.value || 10 })}
              />
            </label>
          </div>
          <div className="nv-cfg__seg">
            {["indoor", "outdoor"].map((m) => (
              <button key={m} className={space.mode === m ? "is-on" : ""}
                onClick={() => setSpace({ ...space, mode: m })}>
                {m === "indoor" ? "Indoor" : "Outdoor · +8%"}
              </button>
            ))}
          </div>
          {R && R.pickup && (
            <div className="nv-cfg__seg" style={{ marginTop: 10 }}>
              <button className={fulfillment === "ship" ? "is-on" : ""}
                onClick={() => setFulfillment("ship")}>Ship to me</button>
              <button className={fulfillment === "collect" ? "is-on" : ""}
                onClick={() => setFulfillment("collect")}>{R.pickupLabel || "Collect"} · Free</button>
            </div>
          )}

          <div className="nv-cfg__h">02 — Finish</div>
          {Object.keys(window.MATERIAL_LINES).length > 1 && (
            <div className="nv-cfg__seg">
              {Object.keys(window.MATERIAL_LINES).map((k) => (
                <button key={k} className={line === k ? "is-on" : ""}
                  onClick={() => { setLine(k); setSub(window.MATERIAL_LINES[k].subs[0].k); }}>
                  {k}
                </button>
              ))}
            </div>
          )}
          <div className="nv-cfg__mini" style={{ marginBottom: 10 }}>{window.MATERIAL_LINES[line].label}</div>
          <div className="nv-cfg__mat">
            {window.MATERIAL_LINES[line].subs.map((s) => (
              <button key={s.k} className={sub === s.k ? "is-on" : ""} onClick={() => setSub(s.k)}>
                <span className="nv-cfg__mat-sw" style={{ background: s.sw }} />
                <span>{s.t}</span>
                <span style={{ color: "var(--ink-3)", fontFamily: "var(--mono)", fontSize: 10 }}>
                  {s.mult === 1 ? "—" : (s.mult > 1 ? `+${Math.round((s.mult - 1) * 100)}%` : `−${Math.round((1 - s.mult) * 100)}%`)}
                </span>
              </button>
            ))}
          </div>

          <div className="nv-cfg__h">03 — Catalog</div>
          <div className="nv-cfg__seg" style={{ marginBottom: 12 }}>
            {filters.map((f) => (
              <button key={f} className={filter === f ? "is-on" : ""} onClick={() => setFilter(f)}>
                {f}
              </button>
            ))}
          </div>
          <div className="nv-cfg__catalog">
            {visibleCatalog.map((c) => (
              <button key={c.id} className="nv-cfg__item" onClick={() => addItem(c.id)}>
                <span className="nv-cfg__item-ic" style={{ background: c.color }} />
                <span style={{ display: "flex", flexDirection: "column", textAlign: "left" }}>
                  <span className="nv-cfg__item-t">{c.t}{c.kit ? ` · ${c.kit.length}-pc kit` : ""}</span>
                  <span className="nv-cfg__item-s">{c.s}</span>
                </span>
                <span className="nv-cfg__item-p">+</span>
              </button>
            ))}
          </div>
        </aside>

        {/* CENTER — scene stage */}
        <div className="nv-cfg__stage">
          <div className="nv-cfg__toolbar">
            <span>Configurator · {window.CFG_VIEWS[view].label} view</span>
            <span className="nv-cfg__views">
              {Object.keys(window.CFG_VIEWS).map((v) => (
                <button
                  key={v}
                  className={view === v ? "is-on" : ""}
                  onClick={() => setView(v)}
                  title={`${window.CFG_VIEWS[v].label} view`}
                >
                  {window.CFG_VIEWS[v].label}
                </button>
              ))}
            </span>
            <span style={{ display: "flex", gap: 16 }}>
              <span>Grid = 1 ft</span>
              <span>Drag to position · Del to remove</span>
            </span>
          </div>
          <div className="nv-cfg__canvas">
            <IsoCanvas
              space={space}
              items={items}
              view={view}
              onMoveGroup={moveGroupTo}
              onSelect={setSelectedId}
              selectedId={selectedId}
              overCap={overCap || anyOutOfBounds}
            />
          </div>
        </div>

        {/* RIGHT — quote */}
        <aside className="nv-cfg__pane">
          <div className="nv-cfg__h">Your Quote</div>
          <div className="nv-cfg__quote">
            {grouped.length === 0 && (
              <div className="nv-cfg__mini">Add items from the catalog to build your quote.</div>
            )}
            {grouped.map((g) => (
              <div key={g.meta.id} className="nv-cfg__line">
                <span>
                  <div className="nv-cfg__line-t">{g.meta.t}</div>
                  <div className="nv-cfg__line-n">{g.meta.s}{g.meta.kit ? ` · ${g.meta.kit.length}-pc kit` : ""}</div>
                </span>
                <span className="nv-cfg__qty">
                  <button onClick={() => removeGroup(g.gids[g.gids.length - 1])}>−</button>
                  <span className="nv-cfg__qty-v">{g.count}</span>
                  <button onClick={() => addItem(g.meta.id)}>+</button>
                </span>
                <span className="nv-cfg__line-p">{fmt(priceOf(g.meta) * g.count * mult)}</span>
              </div>
            ))}
          </div>

          {(overCap || anyOutOfBounds) && (
            <div className="nv-cfg__warn">
              Heads-up: your selection may not fit in a {space.w}′ × {space.d}′ {space.mode} footprint. Resize the space or remove items.
            </div>
          )}

          <div className="nv-cfg__total">
            <div className="nv-cfg__subl"><span>Subtotal</span><span>{fmt(subtotal)}</span></div>
            {space.mode === "outdoor" && (
              <div className="nv-cfg__subl"><span>Outdoor package · +8%</span><span>{fmt(outdoorUpcharge)}</span></div>
            )}
            {quote.fulfillment === "collect" ? (
              <div className="nv-cfg__subl"><span>Collection · UK workshop</span><span>Free</span></div>
            ) : (
              <div className="nv-cfg__subl"><span>Freight (est.)</span><span>{fmt(freight)}</span></div>
            )}
            <div className="nv-cfg__subl"><span>Install &amp; dismantle</span><span>{fmt(install)}</span></div>
            <div className="nv-cfg__totalrow">
              <span className="nv-cfg__totalk">Total · one-time</span>
              <span className="nv-cfg__totalv">{fmt(total)}</span>
            </div>
            {quote.fulfillment === "collect" && R && R.pickupAddress && (
              <div className="nv-cfg__taxnote">Collect from {R.pickupAddress}.</div>
            )}
            {R && R.taxNote && (
              <div className="nv-cfg__taxnote">{R.taxNote}</div>
            )}
          </div>

          <div className="nv-cfg__finance">
            <div className="nv-cfg__finance-k">Or finance at 7.9% APR</div>
            <div className="nv-cfg__finance-v">{fmt(monthly)} <span style={{ fontSize: 12, fontFamily: "var(--mono)", letterSpacing: ".12em", color: "var(--ink-3)" }}>/ MO · {financeMonths} MO</span></div>
            <input type="range" min={12} max={60} step={6} value={financeMonths}
              onChange={(e) => setFinanceMonths(+e.target.value)} />
            <div className="nv-cfg__finance-r">
              <span>12 mo</span><span>36 mo</span><span>60 mo</span>
            </div>
          </div>

          {checkoutNotice === "success" && (
            <div className="nv-cfg__notice nv-cfg__notice--ok">
              Payment received — we’ll be in touch to schedule your build.
            </div>
          )}
          {checkoutNotice === "cancel" && (
            <div className="nv-cfg__notice">Checkout canceled. Your configuration is still here.</div>
          )}
          {checkoutErr && <div className="nv-cfg__notice nv-cfg__notice--err">{checkoutErr}</div>}

          <div className="nv-cfg__ctas">
            <button
              className="nv-btn nv-btn--dark"
              onClick={handleCheckout}
              disabled={checkingOut || checkoutLines.length === 0}
            >
              {checkingOut ? "Starting checkout…" : <>Configure &amp; buy <span className="nv-arrow">→</span></>}
            </button>
            <a className="nv-btn nv-btn--ghost" href="Nova Landing.html#contact">Request a quote instead</a>
          </div>
        </aside>
      </section>
    </div>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(<Configurator />);
