import { Router } from 'express';
import fs from 'fs';
import path from 'path';
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
import { RowDataPacket } from 'mysql2';
import { pool } from '../db';
import {
  db, save, uid, recordUserPackage, saveStripeCustomerId,
  validateCoupon, recordCouponUse, listSavedCards, saveCard, hasUsedFreePlan,
  type AppliedCoupon,
} from '../store';
import { requireAuth, publicUser, AuthedRequest } from '../auth';
import { renderInvoiceEmail, sendEmail } from '../emails';
import { PLANS, getPlan } from '../plans';
import type { Plan } from '../types';
import type Stripe from 'stripe';
import { getStripe, paymentGateway, publishableKey, stripeEnabled, webhookSecret } from '../stripeGateway';

const router = Router();

/** Order numbers are always 6 digits: user_packages.id + this base. */
const ORDER_NO_BASE = 100000;

export interface OrderRow {
  orderNo: number;
  type: 'Plan';
  name: string;
  billingCycle: string;
  domain: string;
  date: string; // ISO
  endDate: string; // ISO — next billing date
  amount: number;
  status: 'Paid' | 'Free';
  billName: string;
  company: string;
  address: string;
  last4: string;
}

/** The user's orders from the user_packages ledger (newest first). */
async function loadOrders(userId: string, domain: string): Promise<OrderRow[]> {
  const [rows] = await pool.query<RowDataPacket[]>(
    `SELECT up.id, up.final_price, up.is_trial_period, up.payment_status,
            COALESCE(up.created_at, up.start_date) AS created_at, up.end_date,
            up.bill_first_name, up.bill_last_name, up.company_name, up.bill_address,
            up.last_digits, pd.name, od.domain AS order_domain
       FROM user_packages up
       LEFT JOIN package_details pd ON pd.id = up.package_id
       LEFT JOIN pdf_order_domains od ON od.user_package_id = up.id
      WHERE up.user_id = ? AND up.deleted_at IS NULL
      ORDER BY up.id DESC`,
    [Number(userId)]
  );
  const iso = (v: unknown) => (v instanceof Date ? v.toISOString() : String(v ?? ''));
  return rows.map((r) => ({
    orderNo: ORDER_NO_BASE + Number(r.id),
    type: 'Plan' as const,
    name: r.name ?? 'Plan',
    billingCycle: 'One-time',
    domain: r.order_domain ?? domain,
    date: iso(r.created_at),
    endDate: iso(r.end_date),
    amount: Number(r.final_price) || 0,
    status: Number(r.final_price) > 0 ? 'Paid' as const : 'Free' as const,
    billName: [r.bill_first_name, r.bill_last_name].filter(Boolean).join(' '),
    company: r.company_name ?? '',
    address: r.bill_address ?? '',
    last4: r.last_digits != null ? String(r.last_digits) : '',
  }));
}

function userDomain(user: { domains: string[]; company: string }): string {
  // No fabricated fallback — orders without a recorded domain (and users who
  // registered without a website) simply have no domain.
  return user.domains[0] ?? '';
}

router.get('/orders', requireAuth, async (req: AuthedRequest, res) => {
  try {
    const user = req.user!;
    const orders = await loadOrders(user.id, userDomain(user));
    res.json({ orders });
  } catch {
    res.status(500).json({ error: 'Could not load orders.' });
  }
});

/** Short date in the reference invoice's style: "Aug 06,2026". */
function invDate(iso: string): string {
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return '—';
  const m = d.toLocaleDateString('en-US', { month: 'short' });
  return `${m} ${String(d.getDate()).padStart(2, '0')},${d.getFullYear()}`;
}

/**
 * PDF invoice matching the ADA dashboard invoice layout:
 * logo + company address | INVOICE meta + PAID badge, Billed To | Subscription,
 * items table with Total/Payments, and a Payments footnote.
 */
router.get('/orders/:orderNo/invoice', requireAuth, async (req: AuthedRequest, res) => {
  const user = req.user!;
  const orders = await loadOrders(user.id, userDomain(user));
  const order = orders.find((o) => o.orderNo === Number(req.params.orderNo));
  if (!order) {
    res.status(404).json({ error: 'Order not found.' });
    return;
  }

  const doc = await PDFDocument.create();
  const page = doc.addPage([595.28, 841.89]); // A4
  const bold = await doc.embedFont(StandardFonts.HelveticaBold);
  const font = await doc.embedFont(StandardFonts.Helvetica);

  const black = rgb(0.13, 0.13, 0.13);
  const gray = rgb(0.45, 0.45, 0.45);
  const lightRule = rgb(0.88, 0.88, 0.88);
  const green = rgb(0.29, 0.68, 0.31);

  const L = 66; // left margin
  const R = 529.28; // right margin edge
  const RCOL = 348; // right column x (INVOICE / Subscription)

  const text = (s: string, x: number, y: number, opts: { size?: number; b?: boolean; color?: ReturnType<typeof rgb> } = {}) =>
    page.drawText(s, { x, y, size: opts.size ?? 10, font: opts.b ? bold : font, color: opts.color ?? black });
  const textR = (s: string, xRight: number, y: number, opts: { size?: number; b?: boolean; color?: ReturnType<typeof rgb> } = {}) => {
    const f = opts.b ? bold : font;
    const size = opts.size ?? 10;
    page.drawText(s, { x: xRight - f.widthOfTextAtSize(s, size), y, size, font: f, color: opts.color ?? black });
  };
  /** Label + bold value pair ("Invoice No # 100014"). */
  const pair = (label: string, value: string, x: number, y: number, size = 10) => {
    text(label, x, y, { size });
    text(value, x + font.widthOfTextAtSize(label, size) + 5, y, { size, b: true });
  };
  const rule = (y: number) =>
    page.drawLine({ start: { x: L, y }, end: { x: R, y }, thickness: 0.7, color: lightRule });

  // ---- Header: logo (left) + INVOICE meta (right) ----
  try {
    const logoBytes = fs.readFileSync(path.join(__dirname, '..', '..', 'resources', 'st-logo.png'));
    const logo = await doc.embedPng(logoBytes);
    const w = 132;
    const h = (logo.height / logo.width) * w;
    page.drawImage(logo, { x: L, y: 776 - h + 34, width: w, height: h });
    // The wordmark SVG lacks the "USA LLC." line the printed logo carries.
    const usaLlc = 'USA LLC.';
    const purple = rgb(0.26, 0, 0.51);
    page.drawText(usaLlc, {
      x: L + (w - bold.widthOfTextAtSize(usaLlc, 9)) / 2, y: 776 - h + 26,
      size: 9, font: bold, color: purple,
    });
  } catch { /* logo optional */ }
  text('3265 Summitrun Drive,', L, 738, { size: 10 });
  text('Independence, KY, 41051.', L, 724, { size: 10 });

  textR('INVOICE', R, 782, { size: 21, b: true });
  pair('Invoice No #', String(order.orderNo), RCOL, 744);
  pair('Invoice Date', invDate(order.date), RCOL, 726);
  pair('Invoice Amount', `$${order.amount.toFixed(2)}`, RCOL, 708);
  pair('Payment Terms', 'Due Upon Receipt', RCOL, 690);
  text(order.status.toUpperCase(), RCOL, 660, { size: 15, b: true, color: green });

  // ---- Billed To | Subscription ----
  rule(636);
  text('Billed To', L, 612, { size: 10, b: true });
  const billName = order.billName || `${user.firstName} ${user.lastName}`;
  const billLines = [billName, order.company || user.company, order.address || user.email].filter(Boolean);
  billLines.forEach((s, i) => text(String(s), L, 594 - i * 16, { size: 10 }));

  text('Subscription', RCOL, 612, { size: 10, b: true });
  pair('Billing Date', invDate(order.date), RCOL, 594);
  pair('Next Billing Date', invDate(order.endDate), RCOL, 578);

  // ---- Items table ----
  const thY = 496;
  rule(thY + 22);
  text('Description', L, thY, { size: 10, b: true });
  textR('Units', 384, thY, { size: 10, b: true });
  textR('Unit Price', 452, thY, { size: 10, b: true });
  textR('Amount (USD)', R, thY, { size: 10, b: true });
  rule(thY - 12);

  const itemY = thY - 34;
  const description = `All in One Accessibility® - AI PDF Remediation - ${order.name}`;
  text(description, L, itemY, { size: 10, b: true });
  textR('1', 384, itemY, { size: 10 });
  textR(`$${order.amount.toFixed(2)}`, 452, itemY, { size: 10 });
  textR(`$${order.amount.toFixed(2)}`, R, itemY, { size: 10, b: true });
  rule(itemY - 14);

  textR('Total', 452, itemY - 36, { size: 11, b: true });
  textR(`$${order.amount.toFixed(2)}`, R, itemY - 36, { size: 11, b: true });
  textR('Payments', 452, itemY - 54, { size: 10, color: gray });
  textR(`$${order.amount.toFixed(2)}`, R, itemY - 54, { size: 10 });

  // ---- Payments footnote ----
  const payY = itemY - 120;
  text('Payments', L, payY, { size: 10, b: true });
  const paidAt = new Date(order.date);
  const hh = String(paidAt.getUTCHours()).padStart(2, '0');
  const mm = String(paidAt.getUTCMinutes()).padStart(2, '0');
  if (order.status === 'Paid') {
    const amount = `$${order.amount.toFixed(2)}`;
    text(amount, L, payY - 22, { size: 10, b: true });
    const rest = order.last4
      ? ` was paid on ${invDate(order.date)} ${hh}:${mm} UTC by card ending ${order.last4}.`
      : ` was paid on ${invDate(order.date)} ${hh}:${mm} UTC.`;
    text(rest, L + bold.widthOfTextAtSize(amount, 10), payY - 22, { size: 10 });
  } else {
    text(`No payment required — ${order.name} was activated free of charge on ${invDate(order.date)}.`, L, payY - 22, { size: 10 });
  }

  doc.setTitle(`Invoice ${order.orderNo}`);
  const bytes = await doc.save();
  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename="invoice-${order.orderNo}.pdf"`);
  res.send(Buffer.from(bytes));
});

/** Email a copy of the invoice (rendered to the outbox). */
router.post('/orders/:orderNo/email', requireAuth, async (req: AuthedRequest, res) => {
  const user = req.user!;
  const orders = await loadOrders(user.id, userDomain(user));
  const order = orders.find((o) => o.orderNo === Number(req.params.orderNo));
  if (!order) {
    res.status(404).json({ error: 'Order not found.' });
    return;
  }
  sendEmail(user, 'invoice', `Your invoice #${order.orderNo} — ${order.name}`, renderInvoiceEmail(user, order));
  res.json({ ok: true, message: `Invoice #${order.orderNo} sent to ${user.email}.` });
});

router.get('/plans', (_req, res) => {
  res.json({ plans: PLANS });
});

/**
 * Plan price after the coupon, validated against the dashboard's `coupons`
 * table — never trust an amount sent by the client.
 *
 * An invalid code is not fatal here: the caller decides whether to reject the
 * purchase or simply charge full price.
 */
async function priceAfterCoupon(
  plan: Plan,
  coupon: unknown,
  userId: string
): Promise<{ amount: number; applied?: AppliedCoupon; reason?: string }> {
  const code = typeof coupon === 'string' ? coupon.trim() : '';
  if (!code) return { amount: plan.price };

  const result = await validateCoupon(code, plan, userId);
  if (!result.ok) return { amount: plan.price, reason: result.reason };

  return {
    amount: Math.max(0, Math.round((plan.price - result.coupon.discount) * 100) / 100),
    applied: result.coupon,
  };
}

/** Checks a coupon without buying anything, so the UI can show the discount. */
router.post('/coupon/validate', requireAuth, async (req: AuthedRequest, res) => {
  const { planId, coupon } = req.body || {};
  const plan = getPlan(planId);
  if (!plan) {
    res.status(400).json({ error: 'Please select a valid plan.' });
    return;
  }
  const result = await validateCoupon(String(coupon || ''), plan, req.user!.id);
  if (!result.ok) {
    res.status(400).json({ error: result.reason });
    return;
  }
  res.json({
    code: result.coupon.code,
    discount: result.coupon.discount,
    total: Math.max(0, Math.round((plan.price - result.coupon.discount) * 100) / 100),
  });
});

/** Which payment gateway the frontend should use. */
router.get('/payment-config', (_req, res) => {
  const gateway = paymentGateway();
  res.json({ gateway, publishableKey: gateway === 'stripe' ? publishableKey() : undefined });
});

/** Stripe: create a PaymentIntent for the selected plan (amount computed here). */
/** Get (or lazily create) the user's Stripe Customer — enables saved cards. */
async function ensureStripeCustomer(user: NonNullable<AuthedRequest['user']>): Promise<string> {
  if (user.stripeCustomerId) return user.stripeCustomerId;
  const customer = await getStripe().customers.create({
    email: user.email,
    name: `${user.firstName} ${user.lastName}`,
    metadata: { userId: user.id },
  });
  await saveStripeCustomerId(user.id, customer.id);
  return customer.id;
}

/** Has this PaymentIntent already activated a plan? (webhook/checkout race + replay guard) */
async function intentAlreadyUsed(paymentIntentId: string): Promise<boolean> {
  const [rows] = await pool.query<RowDataPacket[]>(
    'SELECT 1 FROM user_packages WHERE stripe_payment_intent_id = ? LIMIT 1',
    [paymentIntentId]
  );
  return rows.length > 0;
}

/** Shared plan activation — used by checkout AND the Stripe webhook. */
async function activatePaidPlan(
  user: { id: string; planId: string; pagesRemaining: number; company: string; email: string; phone: string; domains: string[] },
  plan: NonNullable<ReturnType<typeof getPlan>>,
  opts: {
    amount: number;
    billing?: Record<string, string>;
    cardLast4?: string;
    coupon?: string;
    couponApplied?: AppliedCoupon;
    domain?: string;
    paymentIntentId?: string;
    cardUsedId?: number;
    cardType?: string;
    cardFunding?: string;
    cardExpiry?: string;
  }
): Promise<void> {
  db.orders.push({
    id: uid(),
    userId: user.id,
    planId: plan.id,
    amount: opts.amount,
    billing: opts.billing || {},
    cardLast4: opts.cardLast4 || '',
    createdAt: new Date().toISOString(),
  });

  user.planId = plan.id;
  // Free trial keeps its signup balance; paid plans add their page allowance.
  if (plan.id !== 'free') user.pagesRemaining += plan.pages;
  save();

  try {
    await recordUserPackage({
      userId: user.id, plan, kind: plan.price > 0 ? 'purchase' : 'trial', amount: opts.amount,
      billing: opts.billing, cardLast4: opts.cardLast4, coupon: opts.coupon,
      company: user.company, email: user.email, phone: user.phone,
      domain: opts.domain || user.domains[0],
      stripePaymentIntentId: opts.paymentIntentId,
      cardUsedId: opts.cardUsedId,
      cardType: opts.cardType,
      cardFunding: opts.cardFunding,
      cardExpiry: opts.cardExpiry,
      couponId: opts.couponApplied?.id,
    });

    // Redemption is recorded only once the order exists, so a failed payment
    // never burns a per-customer coupon use.
    if (opts.couponApplied) {
      try {
        await recordCouponUse(user.id, opts.couponApplied, null);
      } catch (err) {
        console.error('[billing] Could not record coupon use:', err);
      }
    }
  } catch (err) {
    console.error('[billing] Failed to record user_package:', err);
  }
}

router.post('/create-payment-intent', requireAuth, async (req: AuthedRequest, res) => {
  if (!stripeEnabled()) {
    res.status(409).json({ error: 'Stripe is not enabled.' });
    return;
  }
  const user = req.user!;
  const { planId, coupon, domain, saveCard: keepCard, paymentMethodId } = req.body || {};
  const plan = getPlan(planId);
  if (!plan || plan.price <= 0) {
    res.status(400).json({ error: 'Please select a valid paid plan.' });
    return;
  }
  const { amount, reason } = await priceAfterCoupon(plan, coupon, user.id);
  if (reason) {
    res.status(400).json({ error: reason });
    return;
  }
  try {
    const customerId = await ensureStripeCustomer(user);
    const intent = await getStripe().paymentIntents.create({
      amount: Math.round(amount * 100), // cents
      currency: 'usd',
      customer: customerId,
      // Card-only: matches the CardElement checkout form and avoids
      // redirect-based methods that would require a return_url.
      payment_method_types: ['card'],
      // Save the card on the customer for future one-click payments.
      ...(keepCard ? { setup_future_usage: 'off_session' as const } : {}),
      // Paying with an already-saved card — no card entry needed client-side.
      ...(typeof paymentMethodId === 'string' && paymentMethodId ? { payment_method: paymentMethodId } : {}),
      description: `All in One Accessibility - AI PDF Remediation - ${plan.name}`,
      metadata: {
        userId: user.id,
        planId: plan.id,
        coupon: coupon ? String(coupon) : '',
        domain: typeof domain === 'string' ? domain : (user.domains[0] ?? ''),
      },
    });
    res.json({ clientSecret: intent.client_secret, amount });
  } catch (err) {
    const message = err instanceof Error ? err.message : 'Stripe error';
    console.error('[stripe] create-payment-intent failed:', message);
    res.status(502).json({ error: `Could not start the payment: ${message}` });
  }
});

/** Saved cards of the logged-in user (for one-click repeat payments). */
/**
 * Cards saved for this user.
 *
 * Read from the dashboard's `user_cards` table rather than from Stripe, so both
 * applications show the same cards and agree on which one is default. Both run
 * on the same Stripe account, so a card saved in the dashboard is chargeable
 * here without any migration.
 */
router.get('/payment-methods', requireAuth, async (req: AuthedRequest, res) => {
  try {
    const cards = await listSavedCards(req.user!.id);
    res.json({
      paymentMethods: cards.map((c) => ({
        // The Stripe PaymentMethod id is what a charge is made against; the
        // row id travels separately so the order can reference user_cards.
        id: c.cardId,
        cardRowId: c.id,
        brand: c.brand,
        last4: c.last4,
        expMonth: c.expMonth,
        expYear: c.expYear,
        isDefault: c.isDefault,
      })),
    });
  } catch (err) {
    console.error('[billing] Could not load saved cards:', err);
    res.json({ paymentMethods: [] });
  }
});

router.post('/checkout', requireAuth, async (req: AuthedRequest, res) => {
  const user = req.user!;
  const { planId, billing, card, coupon, domain, paymentIntentId } = req.body || {};
  const plan = getPlan(planId);
  if (!plan) {
    res.status(400).json({ error: 'Please select a valid plan.' });
    return;
  }

  // The free plan is once per user for life — checkout is the other way in
  // besides registration, so it needs the same guard.
  if (plan.price === 0 && (await hasUsedFreePlan(user.id))) {
    res.status(409).json({
      error: 'The free plan can only be used once. Please choose a paid plan.',
      code: 'free_plan_used',
    });
    return;
  }

  let cardLast4 = '';
  let cardType: string | undefined;
  let cardFunding: string | undefined;
  let cardExpiry: string | undefined;
  let cardUsedId: number | undefined;
  const priced = await priceAfterCoupon(plan, coupon, user.id);
  if (priced.reason) {
    res.status(400).json({ error: priced.reason });
    return;
  }
  let amount = priced.amount;

  if (plan.price > 0 && stripeEnabled()) {
    // ---- Stripe gateway: the payment must already be confirmed client-side.
    if (!paymentIntentId || typeof paymentIntentId !== 'string') {
      res.status(400).json({ error: 'Payment was not completed. Please try again.' });
      return;
    }
    try {
      const intent = await getStripe().paymentIntents.retrieve(paymentIntentId, {
        expand: ['latest_charge'],
      });
      if (intent.status !== 'succeeded') {
        res.status(402).json({ error: `Payment not completed (status: ${intent.status}).` });
        return;
      }
      if (intent.metadata?.userId !== user.id || intent.metadata?.planId !== plan.id) {
        res.status(400).json({ error: 'Payment does not match this order.' });
        return;
      }
      // Already activated (e.g. the webhook got there first): succeed idempotently.
      if (await intentAlreadyUsed(paymentIntentId)) {
        res.json({ user: publicUser(user), amount: intent.amount_received / 100, alreadyProcessed: true });
        return;
      }
      // The paid amount is authoritative.
      amount = intent.amount_received / 100;
      const charge = intent.latest_charge;
      if (charge && typeof charge !== 'string') {
        const details = charge.payment_method_details?.card;
        cardLast4 = details?.last4 ?? '';
        cardType = details?.brand ?? undefined;
        cardFunding = details?.funding ?? undefined;
        if (details?.exp_month && details?.exp_year) {
          // Last day of the expiry month, matching how the dashboard stores it.
          cardExpiry = new Date(Date.UTC(details.exp_year, details.exp_month, 0))
            .toISOString()
            .slice(0, 10);
        }
        // Mirror the card into the dashboard's table so both applications offer
        // the same saved cards. Failure here must not fail a paid order.
        const pm = typeof charge.payment_method === 'string' ? charge.payment_method : undefined;
        if (pm && details) {
          try {
            cardUsedId = (await saveCard(user.id, {
              name: charge.billing_details?.name ?? undefined,
              last4: details.last4 ?? '',
              expMonth: details.exp_month ?? 0,
              expYear: details.exp_year ?? 0,
              brand: details.brand ?? 'card',
              funding: details.funding ?? undefined,
              paymentMethodId: pm,
              customerId: typeof intent.customer === 'string' ? intent.customer : undefined,
            })) ?? undefined;
          } catch (err) {
            console.error('[billing] Could not save card to user_cards:', err);
          }
        }
      }
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Stripe error';
      console.error('[stripe] checkout verification failed:', message);
      res.status(502).json({ error: `Could not verify the payment: ${message}` });
      return;
    }
  } else if (plan.price > 0) {
    // ---- Mock gateway (Stripe disabled): format-only validation as before.
    const number = String(card?.number || '').replace(/\s+/g, '');
    const expiry = String(card?.expiry || '');
    const cvv = String(card?.cvv || '');
    if (!/^\d{13,19}$/.test(number)) {
      res.status(400).json({ error: 'Please enter a valid card number.' });
      return;
    }
    if (!/^(0[1-9]|1[0-2])\/\d{2}$/.test(expiry)) {
      res.status(400).json({ error: 'Please enter a valid expiry date (MM/YY).' });
      return;
    }
    if (!/^\d{3,4}$/.test(cvv)) {
      res.status(400).json({ error: 'Please enter a valid CVV.' });
      return;
    }
    if (!billing?.firstName || !billing?.lastName || !billing?.street || !billing?.country || !billing?.zip) {
      res.status(400).json({ error: 'Please complete the billing address.' });
      return;
    }
    cardLast4 = number.slice(-4);
  }

  await activatePaidPlan(user, plan, {
    amount,
    billing,
    cardLast4,
    coupon: typeof coupon === 'string' ? coupon : undefined,
    couponApplied: priced.applied,
    domain: typeof domain === 'string' && domain ? domain : undefined,
    paymentIntentId: typeof paymentIntentId === 'string' ? paymentIntentId : undefined,
    cardUsedId,
    cardType,
    cardFunding,
    cardExpiry,
  });

  res.json({ user: publicUser(user), amount });
});

/**
 * Stripe webhook (raw-body route mounted in index.ts BEFORE express.json).
 * Safety net: activates the plan on payment_intent.succeeded even when the
 * user closed the tab between the card confirmation and our checkout call.
 * Requires STRIPE_WEBHOOK_SECRET; deliveries are idempotent.
 */
export async function handleStripeWebhook(req: { body: Buffer; headers: Record<string, unknown> }, res: {
  status: (code: number) => { json: (body: unknown) => void; send: (body: string) => void };
  json: (body: unknown) => void;
}): Promise<void> {
  const secret = webhookSecret();
  if (!stripeEnabled() || !secret) {
    res.status(501).json({ error: 'Stripe webhook is not configured (STRIPE_WEBHOOK_SECRET).' });
    return;
  }

  let event;
  try {
    const signature = String(req.headers['stripe-signature'] ?? '');
    event = getStripe().webhooks.constructEvent(req.body, signature, secret);
  } catch (err) {
    const message = err instanceof Error ? err.message : 'invalid signature';
    console.warn('[stripe] webhook signature verification failed:', message);
    res.status(400).send(`Webhook Error: ${message}`);
    return;
  }

  if (event.type === 'payment_intent.succeeded') {
    const intent = event.data.object as Stripe.PaymentIntent;
    const { userId, planId, coupon, domain } = intent.metadata ?? {};
    const user = db.users.find((u) => u.id === userId);
    const plan = getPlan(String(planId ?? ''));

    if (!user || !plan) {
      console.warn(`[stripe] webhook: unknown user/plan on ${intent.id} (userId=${userId}, planId=${planId})`);
    } else if (await intentAlreadyUsed(intent.id)) {
      console.log(`[stripe] webhook: ${intent.id} already processed — skipping.`);
    } else {
      // Pull last4 from the charge for the order record.
      let cardLast4 = '';
      try {
        const full = await getStripe().paymentIntents.retrieve(intent.id, { expand: ['latest_charge'] });
        const charge = full.latest_charge;
        if (charge && typeof charge !== 'string') {
          cardLast4 = charge.payment_method_details?.card?.last4 ?? '';
        }
      } catch { /* last4 is cosmetic */ }

      await activatePaidPlan(user, plan, {
        amount: intent.amount_received / 100,
        cardLast4,
        coupon: coupon || undefined,
        domain: domain || undefined,
        paymentIntentId: intent.id,
      });
      console.log(`[stripe] webhook: activated ${plan.id} for user ${user.id} via ${intent.id}`);
    }
  }

  res.json({ received: true });
}

export default router;
