// Cap tracker — "money on the table" view.
// For each credit card, shows current-month progress against its bonus cap,
// remaining headroom, potential miles/cashback, and pace indicator.

function CapTrackerView() {
  const { CARDS } = window.FIN_DATA;
  const credit = CARDS.filter(c => c.type === "credit" && c.bonus);
  const cardData = credit.map(card => computeCapInfo(card));

  // Sort: standard/PPV/Maribank/Lady's first (capped, sorted by SGD headroom desc),
  // then KrisFlyer (gate-based), then PRVI (uncapped) at the bottom
  const ranked = [...cardData].sort((a, b) => {
    const order = { capped: 0, krisflyer: 1, uncapped: 2 };
    if (order[a.tier] !== order[b.tier]) return order[a.tier] - order[b.tier];
    return (b.headroomSgd || 0) - (a.headroomSgd || 0);
  });

  // Aggregates for the headroom strip
  const totalHeadroom = cardData.reduce((s, c) => s + (c.headroomSgd || 0), 0);
  const totalPotentialMiles = cardData.reduce((s, c) => s + (c.potentialMiles || 0), 0);
  const totalPotentialCashback = cardData.reduce((s, c) => s + (c.potentialCashback || 0), 0);

  // Days remaining in current month
  const today = new Date();
  const daysInMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0).getDate();
  const daysLeft = daysInMonth - today.getDate();

  return (
    <div>
      {/* Header */}
      <div style={{ marginBottom: 22 }}>
        <div style={{ fontSize: 11, fontWeight: 600, color: "var(--text-muted)", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: 4 }}>
          Cap tracker
        </div>
        <h1 style={{ fontSize: 24, fontWeight: 700, margin: 0, color: "var(--text-1)", letterSpacing: "-0.015em" }}>
          Money on the table
        </h1>
        <div style={{ fontSize: 13, color: "var(--text-muted)", marginTop: 4 }}>
          {daysLeft === 0 ? "Last day of " : `${daysLeft} day${daysLeft === 1 ? "" : "s"} left in `}
          {today.toLocaleDateString("en-SG", { month: "long" })}
          {" · "}max out caps before month resets
        </div>
      </div>

      {/* Headroom summary strip */}
      <div className="card" style={{ padding: 18, marginBottom: 18, display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 24 }}>
        <Stat label="Unused cap" value={fmt.sgd(totalHeadroom, { decimals: 0 })} sub={`across ${cardData.filter(c => c.tier === "capped").length} capped cards`}/>
        <Stat label="Potential miles" value={fmt.miles(totalPotentialMiles)} sub="if all caps maxed"/>
        <Stat label="Potential cashback" value={fmt.sgd(totalPotentialCashback, { decimals: 0 })} sub="from Maribank overseas"/>
        <Stat label="Days left" value={daysLeft} sub={`of ${daysInMonth} in ${today.toLocaleDateString("en-SG", { month: "short" })}`}/>
      </div>

      {/* Card list */}
      <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        {ranked.map(info => <CapCard key={info.card.id} info={info} daysInMonth={daysInMonth} daysLeft={daysLeft}/>)}
      </div>
    </div>
  );
}

// ─── Per-card computation ───────────────────────────────────────────────────

function computeCapInfo(card) {
  const b = card.bonus;
  const result = {
    card,
    headroomSgd: 0,
    potentialMiles: 0,
    potentialCashback: 0,
    tier: "capped",
  };

  if (b.kind === "krisflyer") {
    const sqSpent = card.sqSpent || 0;
    const gate = b.sqGateSgd || 1000;
    result.tier = "krisflyer";
    result.sqSpent = sqSpent;
    result.gate = gate;
    result.gateRemaining = Math.max(0, gate - sqSpent);
    result.unlocked = sqSpent >= gate;
    result.kind = "krisflyer";
    return result;
  }

  if (b.kind === "prvi") {
    result.tier = "uncapped";
    result.kind = "prvi";
    result.localSpent = card.localSpent || 0;
    result.seaSpent = card.seaSpent || 0;
    result.otherOverseasSpent = card.otherOverseasSpent || 0;
    result.hotelSpent = card.hotelSpent || 0;
    return result;
  }

  if (b.kind === "ppv") {
    const onSpent = card.onlineSpent || 0;
    const clSpent = card.contactlessSpent || 0;
    const onLeft = Math.max(0, b.onlineCapSgd - onSpent);
    const clLeft = Math.max(0, b.contactlessCapSgd - clSpent);
    result.kind = "ppv";
    result.onSpent = onSpent;
    result.clSpent = clSpent;
    result.onCap = b.onlineCapSgd;
    result.clCap = b.contactlessCapSgd;
    result.onLeft = onLeft;
    result.clLeft = clLeft;
    result.headroomSgd = onLeft + clLeft;
    result.potentialMiles = (onLeft + clLeft) * b.rate;
    result.totalSpent = onSpent + clSpent;
    result.totalCap = b.onlineCapSgd + b.contactlessCapSgd;
    return result;
  }

  if (b.kind === "cashback-overseas") {
    const overseas = card.overseasSpent || 0;
    const left = Math.max(0, b.capSgd - overseas);
    result.kind = "cashback-overseas";
    result.overseasSpent = overseas;
    result.cap = b.capSgd;
    result.left = left;
    result.headroomSgd = left;
    result.potentialCashback = left * (b.rate / 100);
    return result;
  }

  // Standard capped: Lady's, Citi Rewards, Trust
  const cap = b.capSgd || 0;
  const spent = card.spent || 0;
  const left = Math.max(0, cap - spent);
  result.kind = "standard";
  result.spent = spent;
  result.cap = cap;
  result.left = left;
  result.headroomSgd = left;
  if (b.kind === "cashback") {
    result.potentialCashback = left * (b.rate / 100);
  } else {
    result.potentialMiles = left * b.rate;
  }
  return result;
}

// ─── Card row ────────────────────────────────────────────────────────────────

function CapCard({ info, daysInMonth, daysLeft }) {
  const { card, kind } = info;
  const accent = card.color || "var(--text-1)";

  return (
    <div className="card" style={{
      padding: 0, overflow: "hidden",
      borderLeft: `3px solid ${accent}`,
      display: "flex", flexDirection: "row",
    }}>
      {/* Mini card visual */}
      <div style={{
        width: 110, flexShrink: 0,
        background: card.color, color: "#fff",
        padding: "16px 14px",
        display: "flex", flexDirection: "column", justifyContent: "space-between",
      }}>
        <div style={{ fontSize: 11, fontWeight: 600, opacity: 0.9, letterSpacing: "0.02em" }}>{card.name}</div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: 6 }}>
          <span style={{ fontSize: 10.5, color: "rgba(255,255,255,0.65)", fontFamily: "var(--font-mono)" }}>····{card.last4}</span>
          <CardBrand network={card.network} size={16}/>
        </div>
      </div>

      {/* Body */}
      <div style={{ flex: 1, padding: "14px 18px", display: "flex", flexDirection: "column", gap: 10 }}>
        {kind === "ppv" && <PpvBody info={info} daysInMonth={daysInMonth} daysLeft={daysLeft}/>}
        {kind === "krisflyer" && <KrisflyerBody info={info}/>}
        {kind === "prvi" && <PrviBody info={info}/>}
        {kind === "standard" && <StandardBody info={info} daysInMonth={daysInMonth} daysLeft={daysLeft}/>}
        {kind === "cashback-overseas" && <MaribankBody info={info} daysInMonth={daysInMonth} daysLeft={daysLeft}/>}
      </div>
    </div>
  );
}

// ─── Per-kind bodies ─────────────────────────────────────────────────────────

function StandardBody({ info, daysInMonth, daysLeft }) {
  const { card, spent, cap, left, potentialMiles, potentialCashback } = info;
  const pct = cap === 0 ? 0 : Math.min(100, (spent / cap) * 100);
  const capped = spent >= cap;
  const isCashback = !!potentialCashback;

  return (
    <>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
        <div style={{ fontSize: 12, color: "var(--text-muted)" }}>{card.bonus.category}</div>
        <PaceBadge spent={spent} cap={cap} daysInMonth={daysInMonth} daysLeft={daysLeft}/>
      </div>
      <ProgressBar pct={pct} color={card.color} capped={capped}/>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", fontVariantNumeric: "tabular-nums" }}>
        <div style={{ fontSize: 13, color: "var(--text-1)", fontWeight: 500 }}>
          {fmt.sgd(spent)} <span style={{ color: "var(--text-muted)", fontWeight: 400 }}>/ {fmt.sgd(cap, { decimals: 0 })}</span>
        </div>
        <div style={{ fontSize: 13, color: capped ? "var(--success)" : "var(--text-1)", fontWeight: 600 }}>
          {capped
            ? "Maxed"
            : `${fmt.sgd(left, { decimals: 0 })} left`}
        </div>
      </div>
      <div style={{ fontSize: 11, color: "var(--text-muted)" }}>
        {capped
          ? "Bonus cap reached. Overflow earns base rate — switch cards."
          : isCashback
          ? `Potential ${fmt.sgd(potentialCashback)} cashback if maxed`
          : `Potential ${fmt.miles(potentialMiles)} miles if maxed`}
      </div>
    </>
  );
}

function PpvBody({ info, daysInMonth, daysLeft }) {
  const { card, onSpent, clSpent, onCap, clCap, onLeft, clLeft, totalSpent, totalCap, potentialMiles } = info;
  const onPct = onCap === 0 ? 0 : Math.min(100, (onSpent / onCap) * 100);
  const clPct = clCap === 0 ? 0 : Math.min(100, (clSpent / clCap) * 100);
  const capped = totalSpent >= totalCap;

  return (
    <>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
        <div style={{ fontSize: 12, color: "var(--text-muted)" }}>Online + Contactless · separate caps</div>
        <PaceBadge spent={totalSpent} cap={totalCap} daysInMonth={daysInMonth} daysLeft={daysLeft}/>
      </div>

      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        <SubLine label="Online" spent={onSpent} cap={onCap} left={onLeft} pct={onPct} color={card.color}/>
        <SubLine label="Contactless" spent={clSpent} cap={clCap} left={clLeft} pct={clPct} color={card.accent}/>
      </div>

      <div style={{ fontSize: 11, color: "var(--text-muted)", marginTop: 2 }}>
        {capped
          ? "Both caps maxed — base rate now."
          : `Combined: ${fmt.sgd(onLeft + clLeft, { decimals: 0 })} left · ${fmt.miles(potentialMiles)} potential miles`}
      </div>
    </>
  );
}

function SubLine({ label, spent, cap, left, pct, color }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
      <div style={{ display: "flex", justifyContent: "space-between", fontSize: 12, fontVariantNumeric: "tabular-nums" }}>
        <div style={{ color: "var(--text-2)", fontWeight: 500 }}>{label}</div>
        <div style={{ color: "var(--text-1)" }}>
          {fmt.sgd(spent)} <span style={{ color: "var(--text-muted)" }}>/ {fmt.sgd(cap, { decimals: 0 })}</span>
          <span style={{ color: "var(--text-muted)", marginLeft: 8 }}>· {fmt.sgd(left, { decimals: 0 })} left</span>
        </div>
      </div>
      <ProgressBar pct={pct} color={color} thin/>
    </div>
  );
}

function KrisflyerBody({ info }) {
  const { card, sqSpent, gate, gateRemaining, unlocked } = info;
  const pct = gate === 0 ? 0 : Math.min(100, (sqSpent / gate) * 100);
  return (
    <>
      <div style={{ fontSize: 12, color: "var(--text-muted)" }}>SQ-related spend gate · unlock 2.4mpd on accelerated categories</div>
      <ProgressBar pct={pct} color={unlocked ? "var(--success)" : card.color}/>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", fontVariantNumeric: "tabular-nums" }}>
        <div style={{ fontSize: 13, color: "var(--text-1)", fontWeight: 500 }}>
          {fmt.sgd(sqSpent)} <span style={{ color: "var(--text-muted)", fontWeight: 400 }}>/ {fmt.sgd(gate, { decimals: 0 })} SQ</span>
        </div>
        <div style={{ fontSize: 13, color: unlocked ? "var(--success)" : "var(--text-1)", fontWeight: 600 }}>
          {unlocked ? "Unlocked" : `${fmt.sgd(gateRemaining, { decimals: 0 })} to unlock`}
        </div>
      </div>
      <div style={{ fontSize: 11, color: "var(--text-muted)" }}>
        {unlocked
          ? "All accelerated categories now earn 2.4mpd for the rest of the membership year."
          : `Spend ${fmt.sgd(gateRemaining, { decimals: 0 })} more on SQ flights, KrisShop, or SilverKris Lounge to unlock.`}
      </div>
    </>
  );
}

function PrviBody({ info }) {
  const { card, localSpent, seaSpent, otherOverseasSpent, hotelSpent } = info;
  const total = localSpent + seaSpent + otherOverseasSpent + hotelSpent;
  const totalMiles =
    localSpent * card.bonus.baseRate +
    seaSpent * card.bonus.seaRate +
    otherOverseasSpent * card.bonus.otherOverseasRate +
    hotelSpent * card.bonus.hotelBookingRate;

  return (
    <>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
        <div style={{ fontSize: 12, color: "var(--text-muted)" }}>No cap · earn unlimited at tiered rates</div>
        <span style={{ fontSize: 11, padding: "3px 8px", borderRadius: 999, background: "var(--success-soft)", color: "var(--success)", fontWeight: 600 }}>Uncapped</span>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: "8px 16px", fontSize: 12, fontVariantNumeric: "tabular-nums" }}>
        <RegionRow label="Local" rate="1.4mpd" spent={localSpent}/>
        <RegionRow label="SEA (MY/ID/TH/VN)" rate="3mpd" spent={seaSpent}/>
        <RegionRow label="Other overseas" rate="2.4mpd" spent={otherOverseasSpent}/>
        <RegionRow label="Agoda/Expedia" rate="up to 8mpd" spent={hotelSpent}/>
      </div>

      <div style={{ fontSize: 11, color: "var(--text-muted)", paddingTop: 4, borderTop: "1px solid var(--border-subtle)" }}>
        Month total: {fmt.sgd(total, { decimals: 0 })} · {fmt.miles(totalMiles)} miles earned · best card for foreign + hotel spend
      </div>
    </>
  );
}

function RegionRow({ label, rate, spent }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between" }}>
      <span style={{ color: "var(--text-2)" }}>{label} <span style={{ color: "var(--text-muted)", fontSize: 11 }}>{rate}</span></span>
      <span style={{ color: "var(--text-1)", fontWeight: 500 }}>{fmt.sgd(spent)}</span>
    </div>
  );
}

function MaribankBody({ info, daysInMonth, daysLeft }) {
  const { card, overseasSpent, cap, left, potentialCashback } = info;
  const pct = cap === 0 ? 0 : Math.min(100, (overseasSpent / cap) * 100);
  const capped = overseasSpent >= cap;
  return (
    <>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
        <div style={{ fontSize: 12, color: "var(--text-muted)" }}>1.5% cashback on overseas spend only</div>
        <PaceBadge spent={overseasSpent} cap={cap} daysInMonth={daysInMonth} daysLeft={daysLeft}/>
      </div>
      <ProgressBar pct={pct} color={card.color} capped={capped}/>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", fontVariantNumeric: "tabular-nums" }}>
        <div style={{ fontSize: 13, color: "var(--text-1)", fontWeight: 500 }}>
          {fmt.sgd(overseasSpent)} <span style={{ color: "var(--text-muted)", fontWeight: 400 }}>overseas / {fmt.sgd(cap, { decimals: 0 })}</span>
        </div>
        <div style={{ fontSize: 13, color: capped ? "var(--success)" : "var(--text-1)", fontWeight: 600 }}>
          {capped ? "Maxed" : `${fmt.sgd(left, { decimals: 0 })} left`}
        </div>
      </div>
      <div style={{ fontSize: 11, color: "var(--text-muted)" }}>
        {capped
          ? "Cap reached. Foreign spend now earns 0%."
          : `Potential ${fmt.sgd(potentialCashback)} cashback if maxed`}
      </div>
    </>
  );
}

// ─── Shared mini-components ──────────────────────────────────────────────────

function ProgressBar({ pct, color, capped, thin }) {
  return (
    <div style={{
      height: thin ? 5 : 7,
      background: "var(--bg-muted)",
      borderRadius: 999,
      overflow: "hidden",
    }}>
      <div style={{
        height: "100%",
        width: pct + "%",
        background: capped ? "var(--success)" : color,
        borderRadius: 999,
        transition: "width 0.3s",
      }}/>
    </div>
  );
}

function PaceBadge({ spent, cap, daysInMonth, daysLeft }) {
  if (cap === 0) return null;
  const elapsed = daysInMonth - daysLeft;
  const expectedPct = elapsed / daysInMonth;
  const actualPct = spent / cap;
  const diff = actualPct - expectedPct;
  const expectedSpent = expectedPct * cap;

  let label, bg, fg;
  if (spent >= cap) {
    label = "Capped";
    bg = "var(--success-soft)"; fg = "var(--success)";
  } else if (diff > 0.05) {
    label = `Ahead ${fmt.sgd(spent - expectedSpent, { decimals: 0 })}`;
    bg = "var(--warning-soft)"; fg = "var(--warning)";
  } else if (diff < -0.05) {
    label = `Behind ${fmt.sgd(expectedSpent - spent, { decimals: 0 })}`;
    bg = "var(--info-soft)"; fg = "var(--info)";
  } else {
    label = "On pace";
    bg = "var(--bg-muted)"; fg = "var(--text-muted)";
  }
  return (
    <span style={{ fontSize: 10.5, padding: "3px 8px", borderRadius: 999, background: bg, color: fg, fontWeight: 600, letterSpacing: "0.01em" }}>
      {label}
    </span>
  );
}

function Stat({ label, value, sub }) {
  return (
    <div>
      <div style={{ fontSize: 11, color: "var(--text-muted)", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: 6 }}>{label}</div>
      <div style={{ fontSize: 22, fontWeight: 700, color: "var(--text-1)", fontVariantNumeric: "tabular-nums", letterSpacing: "-0.01em" }}>{value}</div>
      {sub && <div style={{ fontSize: 11, color: "var(--text-muted)", marginTop: 2 }}>{sub}</div>}
    </div>
  );
}

window.CapTrackerView = CapTrackerView;
