import { NextResponse } from "next/server";
import Stripe from "stripe";
import { verifyStripeWebhookSignature, resolveStripePaymentMethodLabel } from "../../../lib/payments/stripeProvider";
import { sendConfirmedPaymentEmail, type ConfirmPaymentOrderSnapshot } from "../../../lib/payments/confirmPaymentEmail";
import { sendCriticalAlert } from "../../../lib/alertAdmin";

const PAYLOAD_URL = process.env.PAYLOAD_URL || "https://payload.mk360.de";
const PAYMENT_WEBHOOK_SECRET = process.env.PAYMENT_WEBHOOK_SECRET || "";

// Real Stripe webhook — see spicy-leaping-pizza.md §4. Never reachable in
// PAYMENT_TEST_MODE in practice (no real Stripe account sends events
// here then), but left unconditional rather than gated on the env var —
// an invalid/missing signature already fails closed on its own.
export async function POST(request: Request) {
  // Raw body only — request.json() would consume/reparse the stream and
  // Stripe's signature is computed over the exact original bytes.
  const rawBody = await request.text();
  const signature = request.headers.get("stripe-signature");
  if (!signature) return NextResponse.json({ ok: false }, { status: 400 });

  const event = verifyStripeWebhookSignature(rawBody, signature);
  if (!event) return NextResponse.json({ ok: false, reason: "invalid signature" }, { status: 400 });

  if (event.type !== "payment_intent.succeeded" && event.type !== "payment_intent.payment_failed") {
    // Stripe sends many event types we don't act on (e.g.
    // payment_intent.created, charge.*) — ack them so Stripe stops
    // retrying something we were never going to process.
    return NextResponse.json({ ok: true, ignored: event.type });
  }

  const intent = event.data.object as Stripe.PaymentIntent;
  const providerReference = intent.id;
  const orderId = intent.metadata?.orderId;
  const paymentStatus = event.type === "payment_intent.succeeded" ? "paid" : "failed";

  if (!orderId) {
    // stripeProvider.attachOrderMetadata (called right after order
    // creation in /api/checkout) failed to complete for this
    // PaymentIntent — the order's own `providerReference` field is still
    // the source of truth and expirePendingPayments will reconcile it
    // eventually, but that's a multi-hour fallback, not instant. Alert
    // now rather than silently relying on the cleanup job.
    sendCriticalAlert("Stripe-Webhook ohne orderId-Metadaten", { providerReference, paymentStatus, eventType: event.type });
    // Non-2xx so Stripe retries — a later retry might land after the
    // metadata attach (which races the checkout response) has caught up.
    return NextResponse.json({ ok: false, reason: "orderId metadata missing" }, { status: 409 });
  }

  // Best-effort — see resolveStripePaymentMethodLabel's own comment. Only
  // meaningful on the "paid" path; a failed payment never gets a
  // paymentMethodTitle refinement (the order becomes 'cancelled' outright).
  const paymentMethodTitle = paymentStatus === "paid" ? await resolveStripePaymentMethodLabel(intent) : undefined;

  const res = await fetch(`${PAYLOAD_URL}/api/orders/${orderId}/confirm-payment`, {
    method: "POST",
    headers: {
      "x-payment-webhook-secret": PAYMENT_WEBHOOK_SECRET,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      paymentStatus,
      providerReference,
      paidAt: new Date().toISOString(),
      ...(paymentMethodTitle ? { paymentMethodTitle } : {}),
    }),
  }).catch((err) => {
    sendCriticalAlert("confirm-payment-Aufruf ans Backend fehlgeschlagen", { orderId, providerReference, error: String(err) });
    return null;
  });

  if (!res || !res.ok) {
    // Non-2xx on purpose — lets Stripe's own retry schedule (~3 days)
    // provide resilience instead of building an internal retry queue.
    return NextResponse.json({ ok: false }, { status: 502 });
  }

  const data: { ok: boolean; alreadyProcessed?: boolean; order?: ConfirmPaymentOrderSnapshot } = await res.json();

  // Fire-and-forget, same reasoning as the checkout route's own send: a
  // failed confirmation email must never turn an already-successful
  // payment confirmation into a non-2xx response (that would make Stripe
  // retry a webhook we've already fully processed). `alreadyProcessed`/
  // missing `order` means this is a repeat delivery — see confirmPayment.ts's
  // own comment on why the email must not be sent twice.
  if (data.order && !data.alreadyProcessed) {
    void sendConfirmedPaymentEmail(data.order);
  }

  return NextResponse.json({ ok: true });
}
