import { getBillingGateway } from "./gateways/factory.js";
import { BILLING_REGISTRY } from "@nextsparkjs/registries/billing-registry";
import { query, queryOne } from "../db.js";
async function handleStripeWebhook(request, extensions) {
  const payload = await request.text();
  const signature = request.headers.get("stripe-signature");
  if (!signature) {
    return Response.json({ error: "No signature provided" }, { status: 400 });
  }
  let event;
  try {
    const verified = getBillingGateway().verifyWebhookSignature(payload, signature);
    event = { id: verified.id, type: verified.type, data: verified.data };
  } catch (error) {
    console.error("[stripe-webhook] Signature verification failed:", error);
    return Response.json({ error: "Invalid signature" }, { status: 400 });
  }
  const eventId = event.id;
  const existing = await queryOne(
    `SELECT id FROM "billing_events" WHERE metadata->>'stripeEventId' = $1`,
    [eventId]
  );
  if (existing) {
    console.log(`[stripe-webhook] Event ${eventId} already processed, skipping`);
    return Response.json({ received: true, status: "duplicate" });
  }
  try {
    console.log(`[stripe-webhook] Processing event type: ${event.type}`);
    switch (event.type) {
      case "checkout.session.completed":
        await handleCheckoutCompleted(event.data.object, extensions);
        break;
      case "invoice.paid":
        await handleInvoicePaid(event.data.object);
        break;
      case "invoice.payment_failed":
        await handlePaymentFailed(event.data.object);
        break;
      case "customer.subscription.updated":
        await handleSubscriptionUpdated(event.data.object);
        break;
      case "customer.subscription.deleted":
        await handleSubscriptionDeleted(event.data.object);
        break;
      default:
        console.log(`[stripe-webhook] Unhandled event type: ${event.type}`);
    }
    return Response.json({ received: true });
  } catch (error) {
    console.error("[stripe-webhook] Handler error:", error);
    return Response.json({ error: "Handler failed" }, { status: 500 });
  }
}
async function handleCheckoutCompleted(session, extensions) {
  var _a, _b, _c, _d, _e;
  const teamId = ((_a = session.metadata) == null ? void 0 : _a.teamId) || session.client_reference_id;
  if (!teamId) {
    console.warn("[stripe-webhook] checkout.session.completed has no teamId in metadata \u2014 skipping (likely a test/synthetic event)");
    return;
  }
  const planSlug = (_b = session.metadata) == null ? void 0 : _b.planSlug;
  const userId = ((_c = session.metadata) == null ? void 0 : _c.userId) || "system";
  if (!planSlug) {
    if (extensions == null ? void 0 : extensions.onOneTimePaymentCompleted) {
      console.log(`[stripe-webhook] One-time payment for team ${teamId}, delegating to theme extension`);
      const sessionData = {
        id: session.id,
        amountTotal: session.amount_total,
        currency: session.currency,
        customerId: typeof session.customer === "string" ? session.customer : ((_d = session.customer) == null ? void 0 : _d.id) ?? null,
        subscriptionId: typeof session.subscription === "string" ? session.subscription : null,
        metadata: session.metadata ?? {},
        clientReferenceId: session.client_reference_id ?? null
      };
      await extensions.onOneTimePaymentCompleted(sessionData, { teamId, userId });
    } else {
      console.log(`[stripe-webhook] One-time payment for team ${teamId} \u2014 no handler registered, skipping`);
    }
    return;
  }
  const subscriptionId = session.subscription;
  const customerId = session.customer;
  const billingPeriod = ((_e = session.metadata) == null ? void 0 : _e.billingPeriod) || "monthly";
  console.log(`[stripe-webhook] Checkout completed for team ${teamId}, plan: ${planSlug}`);
  let planId = null;
  const planResult = await queryOne(
    `SELECT id FROM plans WHERE slug = $1 LIMIT 1`,
    [planSlug]
  );
  planId = (planResult == null ? void 0 : planResult.id) || null;
  if (!planId) {
    console.warn(`[stripe-webhook] Plan ${planSlug} not found in database, keeping current plan`);
  }
  if (planId) {
    await query(
      `UPDATE subscriptions
       SET "externalSubscriptionId" = $1,
           "externalCustomerId" = $2,
           "paymentProvider" = 'stripe',
           "planId" = $3,
           "billingInterval" = $4,
           status = 'active',
           "updatedAt" = NOW()
       WHERE "teamId" = $5
         AND status IN ('active', 'trialing', 'past_due')`,
      [subscriptionId, customerId, planId, billingPeriod, teamId]
    );
  } else {
    await query(
      `UPDATE subscriptions
       SET "externalSubscriptionId" = $1,
           "externalCustomerId" = $2,
           "paymentProvider" = 'stripe',
           status = 'active',
           "updatedAt" = NOW()
       WHERE "teamId" = $3
         AND status IN ('active', 'trialing', 'past_due')`,
      [subscriptionId, customerId, teamId]
    );
  }
  await logBillingEvent({
    teamId,
    type: "payment",
    status: "succeeded",
    amount: session.amount_total || 0,
    currency: session.currency || "usd",
    stripeEventId: session.id
  });
}
async function handleInvoicePaid(invoice) {
  var _a, _b, _c;
  const expandedInvoice = invoice;
  const subscriptionId = typeof expandedInvoice.subscription === "string" ? expandedInvoice.subscription : (_a = expandedInvoice.subscription) == null ? void 0 : _a.id;
  if (!subscriptionId) {
    console.log("[stripe-webhook] Invoice has no subscription ID, skipping");
    return;
  }
  console.log(`[stripe-webhook] Invoice paid for subscription ${subscriptionId}`);
  if ((_c = (_b = invoice.lines) == null ? void 0 : _b.data) == null ? void 0 : _c[0]) {
    const line = invoice.lines.data[0];
    await query(
      `UPDATE subscriptions
       SET status = 'active',
           "currentPeriodStart" = to_timestamp($1),
           "currentPeriodEnd" = to_timestamp($2),
           "updatedAt" = NOW()
       WHERE "externalSubscriptionId" = $3`,
      [line.period.start, line.period.end, subscriptionId]
    );
  } else {
    await query(
      `UPDATE subscriptions
       SET status = 'active',
           "updatedAt" = NOW()
       WHERE "externalSubscriptionId" = $1`,
      [subscriptionId]
    );
  }
  await syncInvoiceToDatabase(invoice, "paid");
}
async function handlePaymentFailed(invoice) {
  var _a;
  const expandedInvoice = invoice;
  const subscriptionId = typeof expandedInvoice.subscription === "string" ? expandedInvoice.subscription : (_a = expandedInvoice.subscription) == null ? void 0 : _a.id;
  if (!subscriptionId) {
    console.log("[stripe-webhook] Invoice has no subscription ID, skipping");
    return;
  }
  console.log(`[stripe-webhook] Payment failed for subscription ${subscriptionId}`);
  await query(
    `UPDATE subscriptions
     SET status = 'past_due',
         "updatedAt" = NOW()
     WHERE "externalSubscriptionId" = $1`,
    [subscriptionId]
  );
  await syncInvoiceToDatabase(invoice, "failed");
}
async function handleSubscriptionUpdated(subscription) {
  var _a;
  const statusMap = {
    trialing: "trialing",
    active: "active",
    past_due: "past_due",
    canceled: "canceled",
    unpaid: "past_due",
    incomplete: "past_due",
    incomplete_expired: "expired",
    paused: "paused"
  };
  const ourStatus = statusMap[subscription.status] || "active";
  console.log(
    `[stripe-webhook] Subscription updated ${subscription.id}, status: ${subscription.status} -> ${ourStatus}`
  );
  const expandedSubscription = subscription;
  const periodEnd = expandedSubscription.current_period_end ?? null;
  const priceId = (_a = subscription.items.data[0]) == null ? void 0 : _a.price.id;
  let paramIdx = 1;
  const setClauses = [
    `status = $${paramIdx++}`,
    `"cancelAtPeriodEnd" = $${paramIdx++}`
  ];
  const params = [
    ourStatus,
    subscription.cancel_at_period_end
  ];
  if (periodEnd !== null) {
    setClauses.push(`"currentPeriodEnd" = to_timestamp($${paramIdx++})`);
    params.push(periodEnd);
  }
  setClauses.push(`"updatedAt" = NOW()`);
  if (priceId) {
    const planConfig = BILLING_REGISTRY.plans.find(
      (p) => {
        var _a2, _b;
        return ((_a2 = p.providerPriceIds) == null ? void 0 : _a2.monthly) === priceId || ((_b = p.providerPriceIds) == null ? void 0 : _b.yearly) === priceId;
      }
    );
    if (planConfig) {
      const planResult = await queryOne(
        `SELECT id FROM plans WHERE slug = $1 LIMIT 1`,
        [planConfig.slug]
      );
      if (planResult) {
        setClauses.push(`"planId" = $${paramIdx++}`);
        params.push(planResult.id);
      }
    }
  }
  const subIdIdx = paramIdx++;
  params.push(subscription.id);
  await query(
    `UPDATE subscriptions
     SET ${setClauses.join(", ")}
     WHERE "externalSubscriptionId" = $${subIdIdx}`,
    params
  );
}
async function handleSubscriptionDeleted(subscription) {
  console.log(`[stripe-webhook] Subscription deleted ${subscription.id}`);
  await query(
    `UPDATE subscriptions
     SET status = 'canceled',
         "canceledAt" = NOW(),
         "updatedAt" = NOW()
     WHERE "externalSubscriptionId" = $1`,
    [subscription.id]
  );
}
async function logBillingEvent(params) {
  const sub = await queryOne(
    `SELECT id FROM subscriptions WHERE "teamId" = $1 LIMIT 1`,
    [params.teamId]
  );
  if (!sub) {
    console.warn(`[stripe-webhook] No subscription found for team ${params.teamId}, cannot log billing event`);
    return;
  }
  await query(
    `INSERT INTO "billing_events" ("subscriptionId", type, status, amount, currency, metadata)
     VALUES ($1, $2, $3, $4, $5, $6)`,
    [
      sub.id,
      params.type,
      params.status,
      params.amount,
      params.currency,
      JSON.stringify({ stripeEventId: params.stripeEventId })
    ]
  );
}
async function syncInvoiceToDatabase(invoice, status) {
  var _a;
  const expandedInvoice = invoice;
  const subscriptionId = typeof expandedInvoice.subscription === "string" ? expandedInvoice.subscription : (_a = expandedInvoice.subscription) == null ? void 0 : _a.id;
  if (!subscriptionId) {
    console.warn("[stripe-webhook] Invoice has no subscription, cannot sync to invoices table");
    return;
  }
  const subResult = await query(
    `SELECT "teamId" FROM subscriptions WHERE "externalSubscriptionId" = $1`,
    [subscriptionId]
  );
  if (!subResult.rows[0]) {
    console.warn(`[stripe-webhook] No subscription found for ${subscriptionId}, cannot sync invoice`);
    return;
  }
  const teamId = subResult.rows[0].teamId;
  const invoiceNumber = invoice.number || invoice.id;
  const amountInDollars = invoice.total / 100;
  await query(
    `INSERT INTO invoices (
      id, "teamId", "invoiceNumber", date, amount, currency, status, "pdfUrl", description
    ) VALUES (
      gen_random_uuid()::text, $1, $2, to_timestamp($3), $4, $5, $6::invoice_status, $7, $8
    )
    ON CONFLICT ("teamId", "invoiceNumber") DO UPDATE SET
      status = EXCLUDED.status,
      "pdfUrl" = EXCLUDED."pdfUrl",
      "updatedAt" = NOW()`,
    [
      teamId,
      invoiceNumber,
      invoice.created,
      amountInDollars,
      invoice.currency.toUpperCase(),
      status,
      invoice.invoice_pdf || invoice.hosted_invoice_url || null,
      invoice.description || `Invoice ${invoiceNumber}`
    ]
  );
  console.log(`[stripe-webhook] Invoice ${invoiceNumber} synced for team ${teamId} with status ${status}`);
}
export {
  handleStripeWebhook
};
