import { NextRequest, NextResponse } from "next/server";
import { getStripeConfig, getStripeClient } from "@/lib/stripe";
import { db } from "@/lib/db";
import Stripe from "stripe";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

/** Safely extract subscription ID from an invoice object (field moved in basil API types) */
function invoiceSubId(invoice: unknown): string | null {
  const sub = (invoice as Record<string, unknown>)["subscription"];
  if (typeof sub === "string") return sub;
  if (sub && typeof sub === "object") return (sub as { id: string }).id ?? null;
  return null;
}

/** Safely extract current_period_end — field exists at runtime but may be
 *  absent in the "basil" TypeScript types which moved it to item-level. */
function periodEnd(sub: unknown): Date {
  const ts = (sub as Record<string, unknown>)["current_period_end"];
  if (typeof ts === "number") return new Date(ts * 1000);
  // fallback: 30 days from now (will be corrected on next invoice.paid)
  return new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
}

export async function POST(req: NextRequest) {
  const body = await req.text();
  const sig = req.headers.get("stripe-signature");

  const { webhookSecret } = await getStripeConfig();
  if (!webhookSecret) {
    return NextResponse.json({ error: "Webhook secret not configured." }, { status: 500 });
  }
  if (!sig) {
    return NextResponse.json({ error: "Missing stripe-signature header." }, { status: 400 });
  }

  const stripe = await getStripeClient();

  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
  } catch (err) {
    const msg = err instanceof Error ? err.message : "Unknown error";
    console.error("[Stripe Webhook] Signature verification failed:", msg);
    return NextResponse.json({ error: `Webhook Error: ${msg}` }, { status: 400 });
  }

  console.log("[Stripe Webhook]", event.type);

  try {
    switch (event.type) {
      case "checkout.session.completed": {
        const session = event.data.object as Stripe.Checkout.Session;
        const { userId, packageId, currency } = session.metadata ?? {};
        if (!userId || !packageId) break;

        const stripeSubId = session.subscription as string;

        // Retrieve subscription to get billing period — cast through unknown
        // because current_period_end was moved to item-level in the basil API type
        const stripeSub = await stripe.subscriptions.retrieve(stripeSubId);
        const end = periodEnd(stripeSub);

        await db.subscription.upsert({
          where: { userId_packageId: { userId, packageId } },
          create: {
            userId,
            packageId,
            status: "active",
            currency: currency ?? "usd",
            stripeSubscriptionId: stripeSubId,
            stripeCustomerId: session.customer as string,
            currentPeriodEnd: end,
          },
          update: {
            status: "active",
            stripeSubscriptionId: stripeSubId,
            stripeCustomerId: session.customer as string,
            currentPeriodEnd: end,
          },
        });
        break;
      }

      case "invoice.paid": {
        const invoice = event.data.object as Stripe.Invoice;
        const stripeSubId = invoiceSubId(invoice);
        if (!stripeSubId) break;

        // invoice.period_end is always present and correct
        const end = new Date((invoice as unknown as Record<string, number>)["period_end"] * 1000 || Date.now() + 30 * 86400000);

        await db.subscription.updateMany({
          where: { stripeSubscriptionId: stripeSubId },
          data: { status: "active", currentPeriodEnd: end },
        });
        break;
      }

      case "customer.subscription.deleted": {
        const sub = event.data.object as Stripe.Subscription;
        await db.subscription.updateMany({
          where: { stripeSubscriptionId: sub.id },
          data: { status: "cancelled" },
        });
        break;
      }

      case "customer.subscription.updated": {
        const sub = event.data.object as Stripe.Subscription;
        const newStatus =
          sub.cancel_at_period_end
            ? "cancelled"
            : sub.status === "active"
            ? "active"
            : "cancelled";
        const end = periodEnd(sub);

        await db.subscription.updateMany({
          where: { stripeSubscriptionId: sub.id },
          data: { status: newStatus, currentPeriodEnd: end },
        });
        break;
      }

      default:
        break;
    }
  } catch (err) {
    console.error("[Stripe Webhook] Handler error:", err);
    return NextResponse.json({ error: "Webhook handler error." }, { status: 500 });
  }

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