import { NextRequest, NextResponse } from "next/server";
import { connectDB } from "@/lib/db";
import { Order } from "@/models/Order";
import { GiftVoucher } from "@/models/GiftVoucher";
import { getStripeClient } from "@/lib/stripe";
import { logActivity } from "@/lib/orderActivity";
import { generateUniqueVoucherCode, defaultVoucherExpiry } from "@/lib/vouchers";
import { sendVoucherCode } from "@/lib/voucherEmails";
import { checkAndGrantLoyaltyReward } from "@/lib/loyalty";

// Stripe braucht den rohen, unveränderten Request-Body für die
// Signaturprüfung — Next.js darf ihn hier nicht vorher parsen.
export async function POST(req: NextRequest) {
  const stripe = getStripeClient();
  const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
  if (!stripe || !webhookSecret) return new NextResponse("Not configured", { status: 503 });

  const signature = req.headers.get("stripe-signature");
  if (!signature) return new NextResponse("Missing signature", { status: 400 });

  const body = await req.text();
  let event;
  try {
    event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
  } catch (err) {
    console.error("Stripe-Webhook: Signaturprüfung fehlgeschlagen", err);
    return new NextResponse("Invalid signature", { status: 400 });
  }

  if (event.type === "checkout.session.completed") {
    const session = event.data.object;
    const orderId = session.metadata?.orderId;
    const voucherId = session.metadata?.voucherId;
    const paymentKind = session.metadata?.paymentKind;

    if (orderId && session.payment_status === "paid" && paymentKind === "deposit") {
      await connectDB();
      // Analog zum Vollzahlungs-Claim unten: atomar nur greifen, solange die
      // Anzahlung noch nicht als bezahlt markiert ist, gegen doppelt
      // ausgelieferte Stripe-Webhooks.
      const claimed = await Order.findOneAndUpdate(
        { _id: orderId, depositPaid: false },
        { $set: { depositPaid: true, depositPaidAt: new Date() } }
      );
      if (claimed) await logActivity(orderId, "order_paid", "client", "Anzahlung online bezahlt (Stripe)");
    } else if (orderId && session.payment_status === "paid") {
      await connectDB();
      // Atomarer Claim (filtert zusätzlich auf paid:false) statt Lesen-dann-
      // Schreiben — Stripe liefert Webhooks laut eigener Dokumentation
      // gelegentlich doppelt aus; ohne den Claim könnte ein doppelt
      // verarbeitetes Event den Aktivitätsverlauf doppelt eintragen.
      const claimed = await Order.findOneAndUpdate(
        { _id: orderId, paid: false },
        { $set: { paid: true, paidAt: new Date(), paymentMethod: "online" } }
      );
      if (claimed) {
        await logActivity(orderId, "order_paid", "client", "Online bezahlt (Stripe)");
        await checkAndGrantLoyaltyReward(String(claimed.clientId));
      }
    }

    if (voucherId && session.payment_status === "paid") {
      await connectDB();
      const voucher = await GiftVoucher.findById(voucherId);
      if (voucher && !voucher.paid) {
        const code = await generateUniqueVoucherCode();
        // Atomarer Claim, gleiches Prinzip wie beim Auftrag oben — verhindert,
        // dass eine doppelt zugestellte Stripe-Benachrichtigung zwei gültige
        // Gutscheincodes für dieselbe Zahlung erzeugt und verschickt.
        const claimed = await GiftVoucher.findOneAndUpdate(
          { _id: voucherId, paid: false },
          {
            $set: {
              code,
              paid: true,
              paidAt: new Date(),
              paymentMethod: "stripe",
              expiresAt: defaultVoucherExpiry(),
            },
          }
        );
        if (claimed) {
          await sendVoucherCode({
            purchaserName: voucher.purchaserName,
            purchaserEmail: voucher.purchaserEmail,
            recipientName: voucher.recipientName,
            amount: voucher.amount,
            code,
            locale: voucher.locale,
          });
        }
      }
    }
  }

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