import { NextResponse } from 'next/server';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withAuth, AuthedRequest } from '@server/middleware/withAuth';
import { ValidationError } from '@server/errors';
import { getStripeClientFromDb } from '@server/lib/stripe';
import { db, subscriptions, restaurants } from '@server/db/drizzle';
import { eq } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';

export const POST = withErrorHandler(
  withAuth(async (req: AuthedRequest) => {
    await initDatabase();
    const stripe = await getStripeClientFromDb();
    const restaurantId = req.session.restaurantId!;
    const userEmail = req.session.email;

    const subRows = await db.select({ stripeCustomerId: subscriptions.stripeCustomerId })
      .from(subscriptions)
      .where(eq(subscriptions.restaurantId, restaurantId))
      .limit(1);
    const sub = subRows[0];

    let customerId = sub?.stripeCustomerId ?? null;
    if (!customerId) {
      const restRows = await db.select({ name: restaurants.name })
        .from(restaurants)
        .where(eq(restaurants.id, restaurantId));
      const restaurant = restRows[0];
      const customer = await stripe.customers.create({
        email: userEmail ?? undefined,
        name: restaurant?.name ?? undefined,
        metadata: { restaurant_id: restaurantId },
      });
      customerId = customer.id;
      await db.insert(subscriptions)
        .values({ restaurantId, stripeCustomerId: customerId, status: 'active' })
        .onConflictDoUpdate({
          target: subscriptions.restaurantId,
          set: { stripeCustomerId: customerId },
        });
    }

    const setupIntent = await stripe.setupIntents.create({
      customer: customerId,
      payment_method_types: ['card'],
      usage: 'off_session',
    });

    return NextResponse.json({ clientSecret: setupIntent.client_secret });
  })
);

export const PUT = withErrorHandler(
  withAuth(async (req: AuthedRequest) => {
    await initDatabase();
    const stripe = await getStripeClientFromDb();
    const restaurantId = req.session.restaurantId!;
    let _body: Awaited<ReturnType<typeof req.json>>;
    try {
      _body = await req.json();
    } catch {
      throw new ValidationError('Request body must be valid JSON');
    }
    const { paymentMethodId } = _body;
    if (!paymentMethodId) {
      return NextResponse.json({ error: 'paymentMethodId required', code: 'VALIDATION_ERROR' }, { status: 400 });
    }

    const rows = await db.select({
      stripeCustomerId: subscriptions.stripeCustomerId,
      stripeSubscriptionId: subscriptions.stripeSubscriptionId,
    })
    .from(subscriptions)
    .where(eq(subscriptions.restaurantId, restaurantId))
    .limit(1);
    const sub = rows[0];

    if (!sub?.stripeCustomerId) {
      return NextResponse.json({ error: 'No Stripe customer found', code: 'NOT_FOUND' }, { status: 404 });
    }

    const pm = await stripe.paymentMethods.retrieve(paymentMethodId);
    const pmCustomer = typeof pm.customer === 'string' ? pm.customer : pm.customer?.id ?? null;
    if (pmCustomer && pmCustomer !== sub.stripeCustomerId) {
      return NextResponse.json({ error: 'Payment method does not belong to this account', code: 'FORBIDDEN' }, { status: 403 });
    }
    if (!pmCustomer) {
      await stripe.paymentMethods.attach(paymentMethodId, { customer: sub.stripeCustomerId });
    }
    await stripe.customers.update(sub.stripeCustomerId, {
      invoice_settings: { default_payment_method: paymentMethodId },
    });
    if (sub.stripeSubscriptionId) {
      await stripe.subscriptions.update(sub.stripeSubscriptionId, {
        default_payment_method: paymentMethodId,
      });
    }

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