import { NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { stripe } from '@/lib/stripe';
import { getCurrentCustomer } from '@/lib/auth';
import { getEffectivePrice } from '@/lib/pricing';
import { fetchActivePromotions } from '@/lib/promotions';
import { checkRateLimit, getClientIp } from '@/lib/rateLimit';

type CheckoutCartItem = {
  productId: string;
  slug: string;
  name: string;
  color: string;
  size: string | null;
  quantity: number;
  unitPrice: number;
  designJson: { front: unknown[]; back: unknown[] };
  designPreviewUrl: string;
  designPreviewUrlBack: string | null;
  placementPreviewUrl: string | null;
  placementPreviewUrlBack: string | null;
  designWidthCm: number | null;
  designHeightCm: number | null;
};

type GuestInfo = {
  name: string;
  email: string;
  phone: string;
  address: string;
};

export async function POST(req: Request) {
  try {
    if (!process.env.STRIPE_SECRET_KEY) {
      console.error('STRIPE_SECRET_KEY is not configured');
      return NextResponse.json(
        { error: 'Stripe is not configured on this server yet. Add STRIPE_SECRET_KEY to .env.' },
        { status: 500 },
      );
    }

    const ip = getClientIp(req.headers);
    const { allowed } = await checkRateLimit(`checkout:${ip}`, 20, 10 * 60 * 1000);
    if (!allowed) {
      console.error('Rate limit exceeded for IP:', ip);
      return NextResponse.json(
        { error: 'Too many requests — please wait a moment and try again.' },
        { status: 429 },
      );
    }

    const body = await req.json();
    console.log('Checkout request received with items:', body.items?.length, 'customerId:', body.customerId);
    const items: CheckoutCartItem[] = body.items ?? [];
    const guest: GuestInfo | undefined = body.guest;

    if (items.length === 0) {
      return NextResponse.json({ error: 'Your bag is empty.' }, { status: 400 });
    }

    // Never trust the client-submitted unitPrice for the actual charge — look up
    // each product and recompute its current price (including any active sale)
    // server-side. The client's unitPrice is only used for display before this point.
    const products = await prisma.product.findMany({
      where: { id: { in: items.map((i) => i.productId) } },
    });
    const productsById = new Map(products.map((p) => [p.id, p]));

    for (const i of items) {
      if (!productsById.has(i.productId)) {
        return NextResponse.json(
          { error: 'One of the items in your bag is no longer available.' },
          { status: 400 },
        );
      }
    }

    const activePromotions = await fetchActivePromotions();
    const pricedItems = items.map((i) => {
      const design = i.designJson as { front: unknown[]; back: unknown[] };
      const isPersonalised = design.front.length > 0 || design.back.length > 0;
      return {
        ...i,
        unitPrice: getEffectivePrice(productsById.get(i.productId)!, activePromotions, new Date(), isPersonalised).price,
      };
    });

    const total = pricedItems.reduce((sum, i) => sum + i.unitPrice * i.quantity, 0);
    const customer = await getCurrentCustomer();

    // Save the order up front, before redirecting to Stripe — this is what makes
    // the design files (and the order itself) retrievable afterward, whether or
    // not the customer ever makes it back to the success page.
    const order = await prisma.order.create({
      data: {
        status: 'PENDING',
        total,
        customerId: customer?.id,
        email: guest?.email ?? customer?.email,
        guestName: guest?.name,
        guestPhone: guest?.phone,
        items: {
          create: pricedItems.map((i) => ({
            productId: i.productId,
            productName: i.name,
            quantity: i.quantity,
            color: i.color,
            size: i.size,
            unitPrice: i.unitPrice,
            designJson: JSON.stringify(i.designJson),
            designPreviewUrl: i.designPreviewUrl,
            designPreviewUrlBack: i.designPreviewUrlBack,
            placementPreviewUrl: i.placementPreviewUrl,
            placementPreviewUrlBack: i.placementPreviewUrlBack,
            designWidthCm: i.designWidthCm,
            designHeightCm: i.designHeightCm,
          })),
        },
      },
    });

    const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000';

    try {
      console.log('Creating Stripe session for order:', order.id, 'total:', total);
      const session = await stripe.checkout.sessions.create({
        mode: 'payment',
        line_items: pricedItems.map((i) => ({
          price_data: {
            currency: 'gbp',
            unit_amount: i.unitPrice,
            product_data: {
              name: `${i.name} — ${i.color}${i.size ? `, ${i.size}` : ''}`,
            },
          },
          quantity: i.quantity,
        })),
        shipping_address_collection: {
          allowed_countries: ['GB', 'IE', 'US', 'CA', 'AU', 'NZ'],
        },
        customer_email: customer?.email,
        success_url: `${siteUrl}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
        cancel_url: `${siteUrl}/checkout/cancel`,
        metadata: { orderId: order.id },
      });

      console.log('Stripe session created:', session.id, 'URL:', session.url);

      await prisma.order.update({
        where: { id: order.id },
        data: { stripeSessionId: session.id },
      });

      return NextResponse.json({ url: session.url });
    } catch (err) {
      // Stripe call failed — don't leave an orphaned PENDING order with no way to pay it.
      console.error('Stripe session creation failed:', err);
      await prisma.order.delete({ where: { id: order.id } });
      const message = err instanceof Error ? err.message : 'Could not start checkout.';
      console.error('Returning error to client:', message);
      return NextResponse.json({ error: message }, { status: 500 });
    }
  } catch (err) {
    const message = err instanceof Error ? err.message : 'Something went wrong.';
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
