import { NextResponse } from 'next/server';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withAuth, AuthedRequest } from '@server/middleware/withAuth';
import { getStripeClientFromDb } from '@server/lib/stripe';
import { getGatewayConfig } from '@server/services/gateway.service';
import { db, subscriptions, restaurants } from '@server/db/drizzle';
import { eq, sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import type Stripe from 'stripe';

import { ValidationError } from '@server/errors';
import { resolveRequestOrigin } from '@server/lib/request-origin';
import { childLogger } from '@server/logger';
const log = childLogger('route.billing.checkout');

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

    const cfg = await getGatewayConfig();
    if (!cfg.stripeActive) {
      return NextResponse.json({ error: 'Stripe payments are not currently enabled. Please use another payment method.', code: 'GATEWAY_INACTIVE' }, { status: 503 });
    }

    const stripe = await getStripeClientFromDb();
    const restaurantId = req.session.restaurantId!;

    const planRows = await db.execute(sql`
      SELECT p.id, p.name, p.price_monthly, p.price_annual,
             p.stripe_price_id_monthly, p.stripe_price_id_annual,
             COALESCE(pp.price_monthly, p.price_monthly) as effective_price_monthly,
             COALESCE(pp.price_annual, p.price_annual) as effective_price_annual
      FROM plans p
      LEFT JOIN plan_prices pp ON pp.plan_id = p.id AND pp.currency_code = ${cfg.stripeCurrency.toUpperCase()}
      WHERE p.id = ${planId} AND p.is_active = true
      LIMIT 1
    `);
    const plan = planRows.rows[0] as {
      id: string; name: string;
      price_monthly: number; price_annual: number;
      stripe_price_id_monthly: string | null; stripe_price_id_annual: string | null;
      effective_price_monthly: number; effective_price_annual: number;
    } | undefined;

    if (!plan) {
      return NextResponse.json({ error: 'Plan not found', code: 'NOT_FOUND' }, { status: 404 });
    }

    const configuredCurrency = cfg.stripeCurrency.toLowerCase();
    const effectiveUnitAmount = billingCycle === 'annual'
      ? Math.round(parseFloat(String(plan.effective_price_annual)) * 100)
      : Math.round(parseFloat(String(plan.effective_price_monthly)) * 100);

    const fallbackPriceId = billingCycle === 'annual'
      ? plan.stripe_price_id_annual
      : plan.stripe_price_id_monthly;

    const lineItem = configuredCurrency !== 'usd' || !fallbackPriceId
      ? {
          price_data: {
            currency: configuredCurrency,
            unit_amount: effectiveUnitAmount,
            recurring: { interval: billingCycle === 'annual' ? 'year' as const : 'month' as const },
            product_data: { name: plan.name },
          },
          quantity: 1 as const,
        }
      : { price: fallbackPriceId, quantity: 1 as const };

    const priceId = fallbackPriceId;

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

    const restRows = await db.select({ name: restaurants.name })
      .from(restaurants)
      .where(eq(restaurants.id, restaurantId));
    const restaurant = restRows[0];

    const userEmail = req.session.email;

    let customerId = sub?.stripeCustomerId ?? null;
    if (!customerId) {
      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 origin = resolveRequestOrigin(req);

    const existingStripeSubId = sub?.stripeSubscriptionId ?? null;
    if (existingStripeSubId && fallbackPriceId && configuredCurrency === 'usd') {
      try {
        const stripeSub = await stripe.subscriptions.retrieve(existingStripeSubId);
        if (stripeSub.status === 'active' || stripeSub.status === 'trialing') {
          const itemId = stripeSub.items.data[0]?.id;
          if (itemId) {
            const updateItem: Stripe.SubscriptionUpdateParams.Item = { id: itemId, price: fallbackPriceId };
            await stripe.subscriptions.update(existingStripeSubId, {
              items: [updateItem],
              proration_behavior: 'always_invoice',
              metadata: { restaurant_id: restaurantId, plan_id: planId },
            });
            await db.update(subscriptions)
              .set({ planId, stripePriceId: fallbackPriceId })
              .where(eq(subscriptions.restaurantId, restaurantId));
            return NextResponse.json({ redirect: `${origin}/billing?checkout=success` });
          }
        }
      } catch (err) {
        log.warn({ err }, 'Billing: could not update existing subscription, falling through to Checkout');
      }
    }

    if (promoCode?.trim() && !cfg.stripePromoCodesEnabled) {
      return NextResponse.json({ error: 'Promo codes are not enabled', code: 'PROMO_DISABLED' }, { status: 400 });
    }

    let discountsParam: { promotion_code: string }[] | undefined;
    if (promoCode?.trim()) {
      try {
        const promoCodes = await stripe.promotionCodes.list({ code: promoCode.trim(), active: true, limit: 1 });
        if (promoCodes.data.length > 0) {
          discountsParam = [{ promotion_code: promoCodes.data[0].id }];
        } else {
          return NextResponse.json({ error: 'Invalid or expired promo code', code: 'PROMO_INVALID' }, { status: 400 });
        }
      } catch {
        return NextResponse.json({ error: 'Failed to validate promo code', code: 'PROMO_ERROR' }, { status: 400 });
      }
    }

    const session = await stripe.checkout.sessions.create({
      customer: customerId,
      mode: 'subscription',
      line_items: [lineItem],
      success_url: `${origin}/billing?checkout=success&session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${origin}/billing?checkout=cancelled`,
      metadata: { restaurant_id: restaurantId, plan_id: planId },
      subscription_data: {
        metadata: { restaurant_id: restaurantId, plan_id: planId },
      },
      ...(discountsParam ? { discounts: discountsParam } : cfg.stripePromoCodesEnabled ? { allow_promotion_codes: true } : {}),
    });

    return NextResponse.json({ url: session.url });
  })
);
