// ============================================
// Reliq — Billing page
// Step 9: wired to real backend billing API.
//   - Billing summary loaded from GET /v1/billing/summary on mount
//   - Plans loaded from GET /v1/billing/plans (no Stripe price IDs)
//   - Invoices loaded from GET /v1/billing/invoices (real pdfUrl / hostedUrl)
//   - Upgrade / downgrade → POST /v1/billing/change-plan (webhook-first, pending_webhook_confirmation)
//   - Cancel → POST /v1/billing/cancel
//   - Reactivate → POST /v1/billing/reactivate
//   - Billing portal → POST /v1/billing/customer-portal → redirect
//   - localStorage is a synced cache of backend truth (never the source of truth)
//
// Previous: state-aware billing, central plan model, usage limits,
//           upgrade/downgrade, cancel/reactivate, invoice mock actions.
// ============================================

const Billing = ({ go }) => {
  const [tab, setTab]                     = React.useState("plan");
  const [cycle, setCycle]                 = React.useState("monthly");
  const [invoiceSearch, setInvoiceSearch] = React.useState("");
  const [invoiceStatus, setInvoiceStatus] = React.useState("all");

  // Step 9: real data from backend
  const [apiInvoices,  setApiInvoices]  = React.useState(null);   // null = loading
  const [backendPlans, setBackendPlans] = React.useState(null);   // null = not yet loaded
  const [actionLoading, setActionLoading] = React.useState(null); // action key or null

  // ── Billing state reader — covers all 7 product states ─────────────────────
  const readBillingState = () => {
    const sub   = window.reliqSubscription;
    const plans = window.reliqPlans;
    const cache = sub?.getBillingCache?.();

    if (!sub || !plans) {
      return {
        productState: "", planKey: "pro", plan: { name:"Pro", price:79, scanLimit:100 },
        usage: { scansThisMonth:0, appsTracked:0, apiKeys:0, webhooks:0, teamSeats:1 },
        isTrialing: false, isPastDue: false, isCanceled: false, isPendingPayment: false, isActive: true,
        daysLeft: 0, endsStr: "", renewalPrice: "$79/mo",
      };
    }

    const productState   = sub.getProductState();
    const planKey        = plans.getCurrentPlanKey();
    const plan           = plans.getCurrentPlan();

    // Prefer backend usage data when available; fall back to localStorage snapshot.
    let usage;
    if (cache?.usage) {
      usage = {
        scansThisMonth: cache.usage.scans?.used   ?? 0,
        appsTracked:    cache.usage.apps?.used    ?? 0,
        apiKeys:        cache.usage.apiKeys?.used ?? 0,
        webhooks:       cache.usage.webhooks?.used ?? 0,
        teamSeats:      cache.usage.seats?.used   ?? 1,
      };
    } else {
      usage = plans.getUsageSnapshot();
    }

    // Prefer backend plan limits when available.
    const planWithLimits = cache?.limits
      ? { ...plan, scanLimit: cache.limits.scans ?? plan?.scanLimit }
      : plan;

    const isTrialing       = productState === "authenticated_trialing";
    const isPastDue        = productState === "authenticated_past_due";
    const isCanceled       = productState === "authenticated_canceled";
    const isPendingPayment = productState === "authenticated_pending_payment";
    const isActive         = productState === "authenticated_active";
    const daysLeft         = sub.getDaysLeft();
    const endsStr          = sub.getTrialEndStr();
    const renewalPrice     = sub.getRenewalPriceStr();

    // cancelAtPeriodEnd: backend has set cancel_at_period_end on the Stripe subscription.
    // The subscription is still active/trialing but will not renew.
    const cancelAtPeriodEnd  = cache?.subscription?.cancelAtPeriodEnd ?? false;
    const currentPeriodEnd   = cache?.subscription?.currentPeriodEnd  ?? null;

    return { productState, planKey, plan: planWithLimits, usage,
             isTrialing, isPastDue, isCanceled, isPendingPayment, isActive,
             daysLeft, endsStr, renewalPrice,
             cancelAtPeriodEnd, currentPeriodEnd };
  };

  const [bs, setBs] = React.useState(() => readBillingState());
  const refresh     = () => setBs(readBillingState());

  // ── Step 9: load real data from backend on mount ───────────────────────────
  React.useEffect(() => {
    const sub = window.reliqSubscription;

    // Load billing summary → updates cache + localStorage → fires 'reliq-billing-loaded'
    sub?.loadFromBackend?.().then(() => refresh()).catch(() => {});

    // Load plan definitions (no Stripe price IDs)
    window.reliqBillingApi?.getBillingPlans?.()
      .then(data => setBackendPlans(data?.plans || null))
      .catch(() => {});

    // Load real invoices
    window.reliqBillingApi?.listInvoices?.()
      .then(data => setApiInvoices(data?.invoices || []))
      .catch(() => setApiInvoices([]));
  }, []);

  React.useEffect(() => {
    const onRefresh = () => refresh();
    window.addEventListener("focus",                onRefresh);
    window.addEventListener("reliq-auth-change",    onRefresh);
    window.addEventListener("reliq-billing-loaded", onRefresh);
    return () => {
      window.removeEventListener("focus",                onRefresh);
      window.removeEventListener("reliq-auth-change",    onRefresh);
      window.removeEventListener("reliq-billing-loaded", onRefresh);
    };
  }, []);

  const {
    planKey, plan, usage,
    isTrialing, isPastDue, isCanceled, isPendingPayment, isActive,
    daysLeft, endsStr, renewalPrice,
    cancelAtPeriodEnd, currentPeriodEnd,
  } = bs;

  // ── Computed values ─────────────────────────────────────────────────────────
  const canChangePlan = isActive || isTrialing;

  const billingSubText = (() => {
    if (isPendingPayment) return "Payment setup required — add your payment method to activate your trial.";
    if (isPastDue)        return "Payment failed — update your payment method to restore access.";
    if (isCanceled)       return `${plan?.name || "Subscription"} canceled — reactivate to restore access.`;
    if (isTrialing)       return `${plan?.name || ""} trial · ${daysLeft} days left · renews at ${renewalPrice}`;
    const planName  = plan?.name || "Pro";
    const priceStr  = plan?.price ? ` · $${plan.price}/mo` : "";
    return `${planName}${priceStr} · paid via •••• 4242`;
  })();

  // ── Invoice data from backend (or loading state) ────────────────────────────
  const displayInvoices = apiInvoices ?? [];
  const lifetimeSpend   = displayInvoices.reduce((s, inv) => s + (inv.amountCents / 100), 0);
  // Build spend sparkline from last 6 invoices (descending → reverse for chart)
  const spendData = displayInvoices.slice(0, 6)
    .map(inv => inv.amountCents / 100)
    .reverse();
  // Pad to 6 data points for the sparkline
  while (spendData.length < 6) spendData.unshift(0);

  // ── Usage bars ─────────────────────────────────────────────────────────────
  const scansThisMonth = usage?.scansThisMonth ?? 0;
  const scanLimit      = plan?.scanLimit ?? 100;
  const scanPct        = scanLimit === null ? 0 : Math.min(100, (scansThisMonth / scanLimit) * 100);
  const overScanLimit  = scanLimit !== null && scansThisMonth >= scanLimit;
  const nearScanLimit  = !overScanLimit && scanLimit !== null && (scansThisMonth / scanLimit) >= 0.8;
  const scanBarColor   = overScanLimit ? "var(--red)" : nearScanLimit ? "var(--amber)" : "var(--teal)";
  const scanLimitLabel = scanLimit === null ? "∞" : scanLimit;

  const usageBars = [
    { lbl: "Scans this cycle", val: scansThisMonth, max: scanLimitLabel, pct: scanPct, color: scanBarColor, overLimit: overScanLimit, nearLimit: nearScanLimit },
    { lbl: "API calls",        val: usage?.apiKeys  ?? 0, max: 5000, pct: (usage?.apiKeys ?? 0)/5000*100,  color: "var(--blue)" },
    { lbl: "PDF exports",      val: 8,    max: 50,   pct: 8/50*100,   color: "var(--violet)" },
    { lbl: "Storage (builds)", val: 1.4,  max: 5,    pct: 1.4/5*100,  color: "var(--amber)", unit: " GB" },
  ];

  // ── Plan picker — prefer backend definitions, fall back to RELIQ_PLANS ─────
  const PLAN_ORDER  = ["starter", "pro", "team"];
  const currentIdx  = PLAN_ORDER.indexOf(planKey);

  const pickerPlans = PLAN_ORDER.map(pid => {
    if (backendPlans) {
      const bp = backendPlans.find(p => p.key === pid) || {};
      return {
        id:      pid,
        name:    bp.name  || pid,
        price:   bp.priceCents != null ? Math.round(bp.priceCents / 100) : 0,
        scans:   bp.limits?.scans === null ? "Unlimited" : String(bp.limits?.scans ?? "—"),
        seats:   String(bp.limits?.seats ?? 1),
        apiKeys: bp.features?.api_keys || false,
        current: pid === planKey,
      };
    }
    // Fallback to hardcoded local plan model
    const p = (window.RELIQ_PLANS && window.RELIQ_PLANS[pid]) || {};
    return {
      id:      pid,
      name:    p.name  || pid,
      price:   p.price || 0,
      scans:   p.scanLimit === null ? "Unlimited" : String(p.scanLimit ?? "—"),
      seats:   String(p.seats ?? 1),
      apiKeys: (p.apiKeys || 0) > 0,
      current: pid === planKey,
    };
  });

  // ── Action handlers (Step 9: API-backed) ────────────────────────────────────

  const handleUpgrade = (targetPlanKey) => {
    const targetPlan = pickerPlans.find(p => p.id === targetPlanKey);
    if (!targetPlan) return;
    window.reliqConfirm?.show({
      title:        `Upgrade to ${targetPlan.name}?`,
      message:      `Your plan will change to ${targetPlan.name} at $${targetPlan.price}/mo. Changes take effect once Stripe confirms.`,
      confirmLabel: "Upgrade",
      onConfirm:    async () => {
        setActionLoading("upgrade-" + targetPlanKey);
        try {
          await window.reliqBillingApi?.changePlan?.(targetPlanKey);
          window.reliqToast?.success(
            `Plan change to ${targetPlan.name} submitted — your plan will update once Stripe confirms.`
          );
          // Reload billing state after a brief delay (webhook may arrive shortly)
          setTimeout(() => {
            window.reliqSubscription?.loadFromBackend?.().then(() => refresh()).catch(() => {});
          }, 2500);
        } catch (err) {
          window.reliqToast?.error(err.message || "Failed to change plan. Please try again.");
        } finally {
          setActionLoading(null);
        }
      },
    });
  };

  const handleDowngrade = (targetPlanKey) => {
    const targetPlan = pickerPlans.find(p => p.id === targetPlanKey);
    if (!targetPlan) return;
    const currentScans  = usage?.scansThisMonth ?? 0;
    const targetLimit   = targetPlan.scans === "Unlimited" ? null : parseInt(targetPlan.scans, 10);
    const usageWarning  = targetLimit !== null && currentScans > targetLimit
      ? ` Warning: your current usage (${currentScans} scans) exceeds the ${targetPlan.name} limit (${targetLimit} scans/mo). New scans may be paused.`
      : "";
    window.reliqConfirm?.show({
      title:        `Downgrade to ${targetPlan.name}?`,
      message:      `Your plan will change to ${targetPlan.name} at $${targetPlan.price}/mo once Stripe confirms.${usageWarning}`,
      confirmLabel: "Downgrade",
      danger:       !!usageWarning,
      onConfirm:    async () => {
        setActionLoading("downgrade-" + targetPlanKey);
        try {
          await window.reliqBillingApi?.changePlan?.(targetPlanKey);
          window.reliqToast?.success(
            `Plan change to ${targetPlan.name} submitted — your plan will update once Stripe confirms.`
          );
          setTimeout(() => {
            window.reliqSubscription?.loadFromBackend?.().then(() => refresh()).catch(() => {});
          }, 2500);
        } catch (err) {
          window.reliqToast?.error(err.message || "Failed to change plan. Please try again.");
        } finally {
          setActionLoading(null);
        }
      },
    });
  };

  const handleCancelSubscription = () => {
    window.reliqConfirm?.show({
      title:        "Cancel subscription?",
      message:      "Your subscription will be canceled at the end of the current billing cycle. Your scans and reports are preserved.",
      confirmLabel: "Cancel subscription",
      danger:       true,
      onConfirm:    async () => {
        setActionLoading("cancel");
        try {
          await window.reliqBillingApi?.cancelSubscription?.();
          window.reliqToast?.success("Cancellation scheduled. Access continues until the end of your billing cycle.");
          await window.reliqSubscription?.loadFromBackend?.();
          refresh();
        } catch (err) {
          window.reliqToast?.error(err.message || "Failed to cancel subscription. Please try again.");
        } finally {
          setActionLoading(null);
        }
      },
    });
  };

  const handleReactivate = () => {
    // Past due or pending payment → route to payment to update/add method
    if (isPastDue || isPendingPayment) {
      go("/payment");
      return;
    }
    // Canceled with a payment method on file → direct reactivate via API
    window.reliqConfirm?.show({
      title:        "Reactivate subscription?",
      message:      `Your ${plan?.name || ""} plan will be reactivated and billed at the next billing cycle.`,
      confirmLabel: "Reactivate",
      onConfirm:    async () => {
        setActionLoading("reactivate");
        try {
          await window.reliqBillingApi?.reactivateSubscription?.();
          window.reliqToast?.success("Subscription reactivated. Welcome back!");
          await window.reliqSubscription?.loadFromBackend?.();
          refresh();
        } catch (err) {
          window.reliqToast?.error(err.message || "Failed to reactivate. Please try again.");
        } finally {
          setActionLoading(null);
        }
      },
    });
  };

  // Open the Stripe Billing Portal (manages payment methods, billing details)
  const handleOpenPortal = async () => {
    setActionLoading("portal");
    try {
      const result = await window.reliqBillingApi?.openBillingPortal?.();
      if (result?.url) {
        window.location.href = result.url;
      } else {
        window.reliqToast?.error("Could not open billing portal.");
      }
    } catch (err) {
      window.reliqToast?.error(err.message || "Could not open billing portal.");
    } finally {
      setActionLoading(null);
    }
  };

  // Open invoice PDF (new tab) — uses real Stripe URL from backend
  const handleInvoiceDownload = (inv) => {
    if (inv.pdfUrl) {
      window.open(inv.pdfUrl, '_blank', 'noopener,noreferrer');
    } else {
      window.reliqToast?.info("PDF not yet available for this invoice.");
    }
  };

  // Open hosted invoice page (new tab) — uses real Stripe URL from backend
  const handleInvoiceView = (inv) => {
    if (inv.hostedUrl) {
      window.open(inv.hostedUrl, '_blank', 'noopener,noreferrer');
    } else {
      window.reliqToast?.info("Invoice page not yet available.");
    }
  };

  // Refresh billing data from backend
  const handleSyncStripe = async () => {
    setActionLoading("sync");
    try {
      await window.reliqSubscription?.loadFromBackend?.();
      refresh();
      const data = await window.reliqBillingApi?.listInvoices?.();
      setApiInvoices(data?.invoices || []);
      window.reliqToast?.success("Billing data refreshed from backend.");
    } catch (_) {
      window.reliqToast?.info("Could not sync — check your connection.");
    } finally {
      setActionLoading(null);
    }
  };

  const handleDownloadAllInvoices = () => {
    // Open the first available pdfUrl in a new tab as a quick download
    const first = displayInvoices.find(inv => inv.pdfUrl);
    if (first) {
      window.open(first.pdfUrl, '_blank', 'noopener,noreferrer');
    } else {
      window.reliqToast?.info("No invoice PDFs available yet.");
    }
  };

  // ── Header actions (state-aware) ───────────────────────────────────────────
  const headerUpgradeLabel = (() => {
    if (isPastDue)        return "Update payment";
    if (isCanceled)       return "Reactivate";
    if (isPendingPayment) return "Set up payment";
    if (planKey === "team") return "Manage plan";
    return "Upgrade plan";
  })();

  const handleHeaderUpgrade = () => {
    if (isPastDue || isPendingPayment) { go("/payment"); return; }
    if (isCanceled) { handleReactivate(); return; }
    setTab("plan");
  };

  // ── Stats grid computed values ─────────────────────────────────────────────
  const nextInvoiceAmt = (() => {
    if (isPastDue)        return "Past due";
    if (isCanceled)       return "Canceled";
    if (isPendingPayment) return "Pending";
    return `$${plan?.price ?? 79}.00`;
  })();

  const currentPlanStatusText = (() => {
    if (isTrialing)       return `Trial · ${daysLeft}d left`;
    if (isPastDue)        return "Payment failed";
    if (isCanceled)       return "Canceled";
    if (isPendingPayment) return "Setup required";
    return `Active · renews 14 Sep`;
  })();

  const currentPlanStatusColor = (isPastDue || isCanceled || isPendingPayment) ? "var(--red)" : "var(--teal)";

  return (
    <AuthLayout
      route="billing"
      go={go}
      crumbs={["Workspace", "Developer", "Billing"]}
      maxWidth={1180}
      actions={
        <button className="btn btn-sm" onClick={handleDownloadAllInvoices}>
          <Icon name="download" size={13} /> Download all invoices
        </button>
      }
    >
      <div className="page-head">
        <div>
          <h1 className="page-title">Billing</h1>
          <p className="page-sub">{billingSubText}</p>
        </div>
        <div style={{display:"flex", gap:8}}>
          <button className="btn btn-sm" onClick={handleSyncStripe} disabled={actionLoading === "sync"}>
            <Icon name="refresh" size={13} /> {actionLoading === "sync" ? "Syncing…" : "Sync from Stripe"}
          </button>
          <button className="btn btn-sm btn-primary" onClick={handleHeaderUpgrade}>
            <Icon name="bolt" size={13} /> {headerUpgradeLabel}
          </button>
        </div>
      </div>

      {/* ── State banners ─────────────────────────────────────────────────── */}

      {isPendingPayment && (
        <div style={{
          display:"flex", alignItems:"center", gap:14,
          padding:"12px 16px", borderRadius:"var(--r-md)", marginBottom:20,
          background:"var(--bg-2)", border:"1px solid var(--blue)",
        }}>
          <Icon name="lock" size={16} style={{color:"var(--blue)", flexShrink:0}} />
          <div style={{flex:1}}>
            <span style={{fontWeight:600, fontSize:13.5, color:"var(--fg-0)"}}>Payment setup required</span>
            <span style={{color:"var(--fg-2)", fontSize:13, marginLeft:10}}>
              Add your payment method to activate your trial and unlock all paid features.
            </span>
          </div>
          <button className="btn btn-sm btn-primary" onClick={() => go("/payment")}>
            Set up payment <Icon name="arrow-right" size={12} />
          </button>
        </div>
      )}

      {isPastDue && (
        <div style={{
          display:"flex", alignItems:"center", gap:14,
          padding:"12px 16px", borderRadius:"var(--r-md)", marginBottom:20,
          background:"rgba(239,68,68,0.08)", border:"1px solid rgba(239,68,68,0.35)",
        }}>
          <Icon name="alert" size={16} style={{color:"var(--red)", flexShrink:0}} />
          <div style={{flex:1}}>
            <span style={{fontWeight:600, fontSize:13.5, color:"var(--fg-0)"}}>Payment failed</span>
            <span style={{color:"var(--fg-2)", fontSize:13, marginLeft:10}}>
              Your last payment was declined. Update your payment method to restore access.
            </span>
          </div>
          <button className="btn btn-sm" style={{borderColor:"var(--red)", color:"var(--red)"}} onClick={handleOpenPortal} disabled={actionLoading === "portal"}>
            {actionLoading === "portal" ? "Opening…" : "Update payment method"}
          </button>
        </div>
      )}

      {isCanceled && (
        <div style={{
          display:"flex", alignItems:"center", gap:14,
          padding:"12px 16px", borderRadius:"var(--r-md)", marginBottom:20,
          background:"var(--amber-bg)", border:"1px solid var(--line-3)",
        }}>
          <Icon name="x-circle" size={16} style={{color:"var(--amber)", flexShrink:0}} />
          <div style={{flex:1}}>
            <span style={{fontWeight:600, fontSize:13.5, color:"var(--fg-0)"}}>Subscription canceled</span>
            <span style={{color:"var(--fg-2)", fontSize:13, marginLeft:10}}>
              Your access has ended. Reactivate to restore your {plan?.name || ""} plan and all features.
            </span>
          </div>
          <button className="btn btn-sm btn-primary" onClick={handleReactivate} disabled={actionLoading === "reactivate"}>
            {actionLoading === "reactivate" ? "Reactivating…" : "Reactivate"} <Icon name="arrow-right" size={12} />
          </button>
        </div>
      )}

      {isTrialing && (
        <div style={{
          display:"flex", alignItems:"center", gap:14,
          padding:"12px 16px", borderRadius:"var(--r-md)", marginBottom:20,
          background:"var(--teal-bg)", border:"1px solid var(--line-3)",
        }}>
          <Icon name="bolt" size={16} style={{color:"var(--teal)", flexShrink:0}} />
          <div style={{flex:1}}>
            <span style={{fontWeight:600, fontSize:13.5, color:"var(--fg-0)"}}>
              {plan?.name || ""} trial active
            </span>
            <span style={{color:"var(--fg-2)", fontSize:13, marginLeft:10}}>
              {daysLeft} days left · trial ends {endsStr} · renews at {renewalPrice} · cancel anytime
            </span>
          </div>
        </div>
      )}

      {/* ── Cancel-at-period-end banner ─────────────────────────────────────── */}
      {(isActive || isTrialing) && cancelAtPeriodEnd && (
        <div style={{
          display:"flex", alignItems:"center", gap:14,
          padding:"12px 16px", borderRadius:"var(--r-md)", marginBottom:20,
          background:"var(--bg-2)", border:"1px solid var(--amber)",
        }}>
          <Icon name="clock" size={16} style={{color:"var(--amber)", flexShrink:0}} />
          <div style={{flex:1}}>
            <span style={{fontWeight:600, fontSize:13.5, color:"var(--fg-0)"}}>Cancellation scheduled</span>
            <span style={{color:"var(--fg-2)", fontSize:13, marginLeft:10}}>
              Your subscription will not renew
              {currentPeriodEnd
                ? ` — access ends ${new Date(currentPeriodEnd).toLocaleDateString("en-US", { month:"long", day:"numeric", year:"numeric" })}.`
                : " at the end of the current billing period."}
            </span>
          </div>
          <button className="btn btn-sm" onClick={handleReactivate} disabled={actionLoading === "reactivate"}>
            {actionLoading === "reactivate" ? "Reactivating…" : "Reactivate"}
          </button>
        </div>
      )}

      {/* ── Top summary strip ──────────────────────────────────────────────── */}
      <div className="stats-grid">
        <div className="stat">
          <div className="lbl">Current plan</div>
          <div className="val" style={{fontSize:24}}>{plan?.name || "Pro"}</div>
          <div className="trend up" style={{color: currentPlanStatusColor}}>
            <Icon name="check" size={11} /> {currentPlanStatusText}
          </div>
        </div>
        <div className="stat">
          <div className="lbl">Next invoice</div>
          <div className="val">
            {(isPastDue || isCanceled || isPendingPayment)
              ? <span style={{fontSize:18, color:"var(--fg-2)"}}>{nextInvoiceAmt}</span>
              : <>{nextInvoiceAmt.replace("$","")}<span className="unit">.00</span></>
            }
          </div>
          <div className="mono" style={{fontSize:11.5, color:"var(--fg-2)", marginTop:8}}>
            {isTrialing ? `in ${daysLeft} days` : isPastDue ? "Payment required" : isCanceled ? "Subscription ended" : "USD · in 8 days"}
          </div>
        </div>
        <div className="stat">
          <div className="lbl">Lifetime spend</div>
          <div className="val">
            {apiInvoices === null
              ? <span style={{color:"var(--fg-3)"}}>—</span>
              : <>${lifetimeSpend.toFixed(0)}<span className="unit">.00</span></>
            }
          </div>
          <div className="trend up">
            <Icon name="trend-up" size={11} /> across {displayInvoices.length} invoice{displayInvoices.length !== 1 ? "s" : ""}
          </div>
        </div>
        <div className="stat">
          <div className="lbl">Scans this cycle</div>
          <div className="val">
            {scansThisMonth}<span className="unit"> / {scanLimitLabel}</span>
          </div>
          <div className="trend up" style={{color: overScanLimit ? "var(--red)" : nearScanLimit ? "var(--amber)" : undefined}}>
            <Icon name={overScanLimit ? "alert" : "check"} size={11} />
            {" "}{scanLimit === null ? "Unlimited" : `${Math.round(scanPct)}% used`}
            {isTrialing ? ` · ${daysLeft} days left` : " · 8 days left"}
          </div>
          <Sparkline data={[1,3,2,4,3,5,4,3,2,4,3,2]} color={scanBarColor} />
        </div>
      </div>

      <div className="tabs">
        {[
          { id: "plan",     label: "Plan & usage",   ic: "bolt" },
          { id: "method",   label: "Payment method", ic: "lock" },
          { id: "invoices", label: "Invoices",        ic: "file-text", ct: apiInvoices ? String(apiInvoices.length) : null },
          { id: "tax",      label: "Tax & address",   ic: "package" },
        ].map(t => (
          <div key={t.id} className={`tab ${tab === t.id ? "active" : ""}`} onClick={() => setTab(t.id)}>
            <Icon name={t.ic} size={14} />{t.label}
            {t.ct && <span className="ct">{t.ct}</span>}
          </div>
        ))}
      </div>

      {/* ── Plan & usage tab ───────────────────────────────────────────────── */}
      {tab === "plan" && (
        <>
          <div className="two-col" style={{marginBottom:20}}>
            {/* Usage card */}
            <div className="card">
              <div className="card-head">
                <div className="card-title">Cycle usage</div>
                <span className="card-sub">
                  {isTrialing ? `Resets ${endsStr}` : "Resets 14 Sep"} · {isTrialing ? `${daysLeft} days left` : "8 days left"}
                </span>
              </div>
              <div className="card-body" style={{display:"flex", flexDirection:"column", gap:18}}>
                {usageBars.map(u => (
                  <div key={u.lbl}>
                    <div style={{display:"flex", alignItems:"baseline", marginBottom:6}}>
                      <div style={{fontSize:13, color:"var(--fg-1)"}}>{u.lbl}</div>
                      <div style={{marginLeft:"auto", display:"flex", alignItems:"baseline", gap:4}}>
                        <span className="mono" style={{fontSize:14, fontWeight:600, color: u.overLimit ? "var(--red)" : u.nearLimit ? "var(--amber)" : "var(--fg-0)"}}>
                          {u.val}{u.unit || ""}
                        </span>
                        <span className="mono" style={{fontSize:11.5, color:"var(--fg-3)"}}>/ {u.max}{u.unit || ""}</span>
                      </div>
                    </div>
                    <div className="bar-track" style={{height:6, background:"var(--bg-3)", borderRadius:3, overflow:"hidden"}}>
                      <div style={{
                        height:"100%", width:`${u.pct}%`,
                        background:`linear-gradient(90deg, ${u.color}, ${u.color})`,
                        boxShadow:`0 0 6px ${u.color}55`
                      }}></div>
                    </div>
                    {u.overLimit && (
                      <div style={{fontSize:11.5, color:"var(--red)", marginTop:4}}>
                        <Icon name="alert" size={11} /> Over limit — new scans may be paused. <button className="btn btn-sm" style={{padding:"1px 8px", fontSize:11, marginLeft:4}} onClick={() => setTab("plan")}>Upgrade</button>
                      </div>
                    )}
                    {u.nearLimit && !u.overLimit && (
                      <div style={{fontSize:11.5, color:"var(--amber)", marginTop:4}}>
                        <Icon name="alert" size={11} /> Approaching limit — consider upgrading.
                      </div>
                    )}
                  </div>
                ))}
                <div style={{
                  marginTop:6, padding:"12px 14px", borderRadius:10,
                  background:"var(--bg-2)", border:"1px solid var(--line-1)",
                  fontSize:12.5, color:"var(--fg-2)", lineHeight:1.5,
                  display:"flex", alignItems:"center", gap:10
                }}>
                  <Icon name="info" size={14} />
                  Overage scans bill at <strong style={{color:"var(--fg-0)"}}>$1.50/scan</strong>. Set a hard limit in Settings.
                </div>
              </div>
            </div>

            {/* Spend chart */}
            <div className="card">
              <div className="card-head">
                <div className="card-title">Spend history</div>
                <span className="card-sub">Last 6 cycles · USD</span>
              </div>
              <div className="card-body">
                <div style={{display:"flex", alignItems:"flex-end", gap:8, height:140, padding:"8px 0"}}>
                  {spendData.map((v, i) => {
                    const maxV = Math.max(...spendData, 1);
                    const h    = (v / maxV) * 120;
                    const isCur = i === spendData.length - 1;
                    return (
                      <div key={i} style={{flex:1, display:"flex", flexDirection:"column", alignItems:"center", gap:8}}>
                        <div className="mono" style={{fontSize:10.5, color:"var(--fg-3)"}}>${v}</div>
                        <div style={{
                          width:"100%", height:h || 4, borderRadius:"4px 4px 0 0",
                          background: isCur ? "var(--teal)" : "var(--bg-3)",
                          border: isCur ? "1px solid var(--teal-d)" : "1px solid var(--line-1)"
                        }}></div>
                      </div>
                    );
                  })}
                </div>
                <div style={{display:"flex", gap:8, marginTop:6}}>
                  {["Apr","May","Jun","Jul","Aug","Sep"].map(m => (
                    <div key={m} className="mono" style={{flex:1, textAlign:"center", fontSize:10.5, color:"var(--fg-3)", letterSpacing:"0.06em", textTransform:"uppercase"}}>{m}</div>
                  ))}
                </div>
              </div>
            </div>
          </div>

          {/* Plan picker */}
          <div className="card">
            <div className="card-head">
              <div className="card-title">Change plan</div>
              <span className="card-sub">Billed monthly · cancel any time</span>
              <div style={{marginLeft:"auto", display:"inline-flex", gap:2, background:"var(--bg-2)", borderRadius:999, padding:3, fontFamily:"var(--font-mono)", fontSize:11.5}}>
                <span
                  onClick={() => setCycle("monthly")}
                  style={{padding:"4px 12px", borderRadius:999, cursor:"pointer",
                    background: cycle === "monthly" ? "var(--bg-1)" : "transparent",
                    color: cycle === "monthly" ? "var(--fg-0)" : "var(--fg-2)",
                    boxShadow: cycle === "monthly" ? "0 1px 2px rgba(15,17,21,0.06)" : "none"}}>Monthly</span>
                <span
                  onClick={() => setCycle("annual")}
                  style={{padding:"4px 12px", borderRadius:999, cursor:"pointer",
                    background: cycle === "annual" ? "var(--bg-1)" : "transparent",
                    color: cycle === "annual" ? "var(--fg-0)" : "var(--fg-2)",
                    boxShadow: cycle === "annual" ? "0 1px 2px rgba(15,17,21,0.06)" : "none"}}>Annual <span style={{color:"var(--teal)"}}>· save 20%</span></span>
              </div>
            </div>
            <div className="card-body" style={{padding:0}}>
              {!canChangePlan && (
                <div style={{
                  padding:"10px 20px", borderBottom:"1px solid var(--line-1)",
                  background:"var(--bg-2)", fontSize:12.5, color:"var(--fg-2)",
                  display:"flex", alignItems:"center", gap:8
                }}>
                  <Icon name="lock" size={13} style={{color:"var(--fg-3)"}} />
                  {isPastDue ? "Resolve your payment issue before changing plans." :
                   isCanceled ? "Reactivate your subscription to change plans." :
                   "Set up your payment method to change plans."}
                </div>
              )}
              <div className="pricing-grid" style={{gap:0}}>
                {pickerPlans.map((p, i) => {
                  const targetIdx  = PLAN_ORDER.indexOf(p.id);
                  const isUpgrade  = targetIdx > currentIdx;
                  const displayPrice = cycle === "annual" ? Math.round(p.price * 0.8) : p.price;
                  const isActing   = actionLoading === "upgrade-" + p.id || actionLoading === "downgrade-" + p.id;
                  return (
                    <div key={p.id} className={`price-card ${p.current ? "feat" : ""}`} style={{
                      borderRadius:0, borderTop:0, borderBottom:0,
                      borderLeft: i === 0 ? 0 : "1px solid var(--line-1)",
                      borderRight:0, background:"transparent"
                    }}>
                      {p.current && <span className="ribbon">Current plan</span>}
                      <h3>{p.name}</h3>
                      <div className="price">
                        ${displayPrice}<span className="unit">/mo</span>
                      </div>
                      {cycle === "annual" && (
                        <div className="mono" style={{fontSize:11, color:"var(--fg-3)", marginTop:-2, marginBottom:8}}>
                          billed ${Math.round(p.price * 0.8 * 12)} yearly
                        </div>
                      )}
                      <ul>
                        <li><Icon name="check" size={14} className="ck" /> {p.scans} scans per month</li>
                        <li><Icon name="check" size={14} className="ck" /> Both stores</li>
                        <li>
                          {p.apiKeys
                            ? <Icon name="check" size={14} className="ck" />
                            : <Icon name="x" size={14} style={{color:"var(--fg-3)"}} />}
                          <span style={{color: p.apiKeys ? "var(--fg-1)" : "var(--fg-3)", textDecoration: p.apiKeys ? "none" : "line-through"}}>API access</span>
                        </li>
                        <li><Icon name="check" size={14} className="ck" /> {p.seats} {p.seats === "1" ? "seat" : "seats"}</li>
                      </ul>
                      {p.current ? (
                        <button className="btn" disabled style={{opacity:0.5}}>Current plan</button>
                      ) : !canChangePlan ? (
                        <button className="btn" disabled style={{opacity:0.4}}>
                          {isUpgrade ? `Upgrade to ${p.name}` : `Downgrade to ${p.name}`}
                        </button>
                      ) : isUpgrade ? (
                        <button className="btn btn-primary" onClick={() => handleUpgrade(p.id)} disabled={isActing}>
                          {isActing ? "Submitting…" : <><Icon name="bolt" size={12} /> Upgrade to {p.name}</>}
                        </button>
                      ) : (
                        <button className="btn" onClick={() => handleDowngrade(p.id)} disabled={isActing}>
                          {isActing ? "Submitting…" : `Downgrade to ${p.name}`}
                        </button>
                      )}
                    </div>
                  );
                })}
              </div>
              {/* Cancel subscription — only shown for active/trialing */}
              {canChangePlan && (
                <div style={{
                  padding:"12px 20px", borderTop:"1px solid var(--line-1)",
                  display:"flex", alignItems:"center", justifyContent:"flex-end"
                }}>
                  <button
                    className="btn btn-sm btn-ghost"
                    style={{color:"var(--red)", borderColor:"transparent"}}
                    onClick={handleCancelSubscription}
                    disabled={actionLoading === "cancel"}
                  >
                    {actionLoading === "cancel" ? "Canceling…" : "Cancel subscription"}
                  </button>
                </div>
              )}
            </div>
          </div>
        </>
      )}

      {/* ── Payment method tab ─────────────────────────────────────────────── */}
      {tab === "method" && (
        <div style={{display:"grid", gridTemplateColumns:"1.4fr 1fr", gap:16}}>
          <div className="card">
            <div className="card-head">
              <div className="card-title">Payment methods</div>
              {/* Step 9: open Stripe Billing Portal to manage payment methods */}
              <button className="btn btn-sm" style={{marginLeft:"auto"}} onClick={handleOpenPortal} disabled={actionLoading === "portal"}>
                <Icon name="external-link" size={12} /> {actionLoading === "portal" ? "Opening…" : "Manage in Stripe"}
              </button>
            </div>
            <div style={{
              padding:"40px 24px", textAlign:"center",
              display:"flex", flexDirection:"column", alignItems:"center", gap:14,
              color:"var(--fg-2)", fontSize:13.5,
            }}>
              <Icon name="lock" size={28} style={{color:"var(--fg-3)"}} />
              <div>
                <div style={{fontWeight:600, color:"var(--fg-1)", marginBottom:6}}>Managed by Stripe</div>
                <div style={{lineHeight:1.6}}>
                  Payment methods, billing details, and invoices are managed<br />
                  securely via Stripe. Open the billing portal to make changes.
                </div>
              </div>
              <button className="btn btn-primary btn-sm" onClick={handleOpenPortal} disabled={actionLoading === "portal"}>
                <Icon name="external-link" size={13} /> {actionLoading === "portal" ? "Opening portal…" : "Open billing portal"}
              </button>
            </div>
          </div>

          <div className="card">
            <div className="card-head">
              <div className="card-title">Billing details</div>
            </div>
            <div className="card-body">
              <div className="field">
                <label>Billing email</label>
                <input defaultValue="billing@roamly.app" />
              </div>
              <div className="field">
                <label>Receipts</label>
                <div style={{display:"flex", alignItems:"center", gap:10, padding:"10px 12px", background:"var(--bg-2)", borderRadius:6, border:"1px solid var(--line-1)"}}>
                  <div className="toggle on"></div>
                  <span style={{fontSize:13}}>Email receipt to billing contact on every charge</span>
                </div>
              </div>
              <div className="field">
                <label>Hard usage limit</label>
                <select>
                  <option>Pause overage at {scanLimitLabel} scans</option>
                  <option>Allow up to {scanLimit === null ? 600 : Math.round((plan?.scanLimit || 100) * 1.1)} scans then pause</option>
                  <option>No limit (auto-bill overages)</option>
                </select>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* ── Invoices tab ───────────────────────────────────────────────────── */}
      {tab === "invoices" && (() => {
        // Filter by search and status
        const filtered = displayInvoices.filter(inv => {
          if (invoiceSearch) {
            const q = invoiceSearch.toLowerCase();
            const num  = (inv.number || '').toLowerCase();
            const curr = (inv.currency || '').toLowerCase();
            const stat = (inv.status  || '').toLowerCase();
            if (!num.includes(q) && !curr.includes(q) && !stat.includes(q)) return false;
          }
          if (invoiceStatus !== "all") {
            const statusMap = { paid: "PAID", open: "OPEN", failed: "UNCOLLECTIBLE" };
            if (inv.status !== statusMap[invoiceStatus]) return false;
          }
          return true;
        });

        return (
          <div className="card">
            <div className="card-head">
              <div className="card-title">Invoices</div>
              <span className="card-sub" style={{marginRight:"auto"}}>All amounts · paid via Stripe</span>
              <div style={{position:"relative"}}>
                <Icon name="search" size={13} style={{position:"absolute", left:9, top:"50%", transform:"translateY(-50%)", color:"var(--fg-3)", pointerEvents:"none"}} />
                <input
                  value={invoiceSearch}
                  onChange={e => setInvoiceSearch(e.target.value)}
                  placeholder="Search invoices…"
                  style={{height:32, width:190, boxSizing:"border-box",
                    background:"var(--bg-2)", border:"1px solid var(--line-2)", borderRadius:"var(--r-sm)",
                    padding:"0 10px 0 30px", fontSize:12.5, color:"var(--fg-0)", outline:"none"}}
                />
              </div>
              <select value={invoiceStatus} onChange={e => setInvoiceStatus(e.target.value)} style={{
                background:"var(--bg-2)", border:"1px solid var(--line-2)", borderRadius:"var(--r-sm)",
                padding:"0 10px", height:32, fontSize:12.5, color:"var(--fg-0)", outline:"none", cursor:"pointer",
              }}>
                <option value="all">All statuses</option>
                <option value="paid">Paid</option>
                <option value="open">Open</option>
                <option value="failed">Failed</option>
              </select>
              {(invoiceSearch || invoiceStatus !== "all") && (
                <button className="btn btn-sm btn-ghost" onClick={() => { setInvoiceSearch(""); setInvoiceStatus("all"); }}>
                  <Icon name="x" size={11} />
                </button>
              )}
            </div>
            <table className="tbl">
              <thead>
                <tr>
                  <th>Invoice</th>
                  <th>Date</th>
                  <th>Description</th>
                  <th style={{textAlign:"right"}}>Amount</th>
                  <th>Status</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {apiInvoices === null && (
                  <tr><td colSpan={6} style={{textAlign:"center", padding:"40px 20px", color:"var(--fg-3)"}}>
                    Loading invoices…
                  </td></tr>
                )}
                {apiInvoices !== null && filtered.length === 0 && (
                  <tr><td colSpan={6} style={{textAlign:"center", padding:"40px 20px", color:"var(--fg-3)"}}>
                    {displayInvoices.length === 0 ? "No invoices yet." : "No invoices match your search."}
                  </td></tr>
                )}
                {filtered.map(inv => (
                  <tr key={inv.id}>
                    <td className="mono" style={{fontSize:12.5, color:"var(--fg-1)"}}>{inv.number || inv.id}</td>
                    <td className="mono" style={{fontSize:12.5, color:"var(--fg-2)"}}>
                      {new Date(inv.issuedAt).toLocaleDateString("en-GB", { day:"2-digit", month:"short", year:"numeric" })}
                    </td>
                    <td style={{fontSize:13}}>{plan?.name || "Subscription"} · {inv.currency?.toUpperCase()}</td>
                    <td style={{textAlign:"right", fontFamily:"var(--font-mono)", fontSize:13.5, fontWeight:500}}>
                      ${(inv.amountCents / 100).toFixed(2)}
                    </td>
                    <td>
                      <span className="chip" style={{color:"var(--fg-0)", borderColor:"var(--line-3)", background:"var(--bg-2)"}}>
                        <Icon name={inv.status === "PAID" ? "check" : "alert"} size={10} />
                        {" "}{inv.status === "PAID" ? "Paid" : inv.status === "OPEN" ? "Open" : inv.status}
                      </span>
                    </td>
                    <td>
                      <div style={{display:"flex", gap:4, justifyContent:"flex-end"}}>
                        <button
                          className="btn btn-sm btn-ghost btn-icon"
                          title={inv.pdfUrl ? "Download PDF" : "PDF not yet available"}
                          onClick={() => handleInvoiceDownload(inv)}
                          style={{opacity: inv.pdfUrl ? 1 : 0.4}}
                        >
                          <Icon name="download" size={13} />
                        </button>
                        <button
                          className="btn btn-sm btn-ghost btn-icon"
                          title={inv.hostedUrl ? "View invoice" : "Invoice page not yet available"}
                          onClick={() => handleInvoiceView(inv)}
                          style={{opacity: inv.hostedUrl ? 1 : 0.4}}
                        >
                          <Icon name="external-link" size={13} />
                        </button>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        );
      })()}

      {/* ── Tax & address tab ──────────────────────────────────────────────── */}
      {tab === "tax" && (
        <div className="card">
          <div className="card-head">
            <div className="card-title">Tax & billing address</div>
            <span className="card-sub" style={{marginLeft:"auto"}}>
              <Icon name="info" size={12} style={{verticalAlign:"middle", marginRight:4}} />
              Tax details and VAT are managed in the Stripe billing portal.
              <button className="btn btn-sm btn-ghost" style={{marginLeft:8}} onClick={handleOpenPortal} disabled={actionLoading === "portal"}>
                <Icon name="external-link" size={12} /> Open portal
              </button>
            </span>
          </div>
          <div className="card-body" style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:18}}>
            <div className="field">
              <label>Legal entity name</label>
              <input defaultValue="Roamly Ltd" />
            </div>
            <div className="field">
              <label>Tax ID / VAT number</label>
              <input defaultValue="ZA 4920185931" />
              <div className="hint">South African VAT registration · validated</div>
            </div>
            <div className="field" style={{gridColumn:"1 / -1"}}>
              <label>Address line 1</label>
              <input defaultValue="14 Bree Street, Suite 302" />
            </div>
            <div className="field">
              <label>City</label>
              <input defaultValue="Cape Town" />
            </div>
            <div className="field">
              <label>Postal code</label>
              <input defaultValue="8001" />
            </div>
            <div className="field">
              <label>Country</label>
              <select><option>South Africa</option><option>United States</option><option>United Kingdom</option></select>
            </div>
            <div className="field">
              <label>State / Province</label>
              <input defaultValue="Western Cape" />
            </div>
            <div style={{gridColumn:"1 / -1", display:"flex", justifyContent:"flex-end", gap:8}}>
              <button className="btn">Cancel</button>
              <button className="btn btn-primary" onClick={() => window.reliqToast?.success("Billing address saved (mock).")}>Save changes</button>
            </div>
          </div>
        </div>
      )}
    </AuthLayout>
  );
};

Object.assign(window, { Billing });
