// src/lib/billing/seranking-fulfillment.ts — SE-Ranking sync-pack webhook
// fulfillment (monétisation slice 2). Mirror of pack-fulfillment.ts (MCP
// credit packs, 3C): called from the Stripe plugin's `onEvent` hook
// (src/lib/auth.ts) for EVERY Stripe event, only `checkout.session.completed`
// one-off `seranking_pack` purchases are handled, everything else is ignored.
// Idempotent by construction — the purchase row is keyed UNIQUE on
// stripeSessionId and the balance is only credited when the insert actually
// happened (webhook retries are no-ops).
import "server-only";
import { randomUUID } from "node:crypto";
import type Stripe from "stripe";
import { db } from "@/lib/db/client";
import { serankingSyncPurchases } from "@/lib/db/domain-schema";
import { creditSyncs } from "@/lib/seranking/balance";

const DEFAULT_PACK_SYNCS = 100;
const DEFAULT_PACK_AMOUNT_CENTS = 999;

/** Credits the org's SE-Ranking sync balance for a completed pack Checkout
 *  (idempotent). */
export async function fulfillSerankingPack(event: Stripe.Event): Promise<void> {
  if (event.type !== "checkout.session.completed") return;
  const session = event.data.object;
  if (session.mode !== "payment") return;
  const metadata = session.metadata;
  if (metadata?.type !== "seranking_pack") return;
  const orgId = metadata.orgId;
  if (!orgId) return;

  // Clamp: metadata is attacker-controllable (Stripe Checkout metadata can be
  // tampered with client-side before session creation in some flows) — a
  // negative or fractional value must never reach creditSyncs. Floor to an
  // integer, floor at 1 (never credit zero/negative).
  const syncs = Math.max(
    1,
    Math.floor(Number(metadata.syncs) || DEFAULT_PACK_SYNCS),
  );
  const amountCents = session.amount_total ?? DEFAULT_PACK_AMOUNT_CENTS;

  await db.transaction(async (tx) => {
    const inserted = await tx
      .insert(serankingSyncPurchases)
      .values({
        id: randomUUID(),
        orgId,
        stripeSessionId: session.id,
        syncs,
        amountCents,
      })
      .onConflictDoNothing({ target: serankingSyncPurchases.stripeSessionId })
      .returning({ id: serankingSyncPurchases.id });
    // Only credit the balance when the purchase row was actually inserted —
    // a webhook retry hits the UNIQUE conflict and returns no row.
    if (inserted.length > 0) {
      await creditSyncs(orgId, syncs, tx);
    }
  });
}
