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 { getGatewayConfig } from '@server/services/gateway.service';
import { db, subscriptions } from '@server/db/drizzle';
import { eq, sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import { childLogger } from '@server/logger';
import Stripe from 'stripe';

const log = childLogger('route.billing.proration-preview');

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 } = _body;
    if (!planId) {
      return NextResponse.json({ error: 'planId is required' }, { status: 400 });
    }

    const cfg = await getGatewayConfig();
    const configuredCurrency = cfg.stripeCurrency.toUpperCase();

    if (!cfg.stripeActive) {
      return NextResponse.json({ prorationAmount: null, currency: configuredCurrency, lines: [], noSubscription: true });
    }

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

    if (!sub?.stripeSubscriptionId || !sub.stripeCustomerId) {
      return NextResponse.json({ prorationAmount: null, currency: configuredCurrency, lines: [], noSubscription: true });
    }

    const planRowsResult = await db.execute(sql`
      SELECT p.id, p.name, 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 = ${configuredCurrency}
      WHERE p.id = ${planId} AND p.is_active = true
      LIMIT 1
    `);
    type PlanRow = { id: string; name: string; stripe_price_id_monthly: string | null; stripe_price_id_annual: string | null; effective_price_monthly: number; effective_price_annual: number };
    const plan = planRowsResult.rows[0] as PlanRow | undefined;

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

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

    const effectiveAmount = billingCycle === 'annual'
      ? parseFloat(String(plan.effective_price_annual))
      : parseFloat(String(plan.effective_price_monthly));

    if (!fallbackPriceId || configuredCurrency !== 'USD') {
      return NextResponse.json({
        prorationAmount: effectiveAmount,
        currency: configuredCurrency,
        lines: [{ description: `${plan.name} plan (${billingCycle})`, amount: effectiveAmount, currency: configuredCurrency }],
        noSubscription: false,
        staticPreview: true,
      });
    }

    try {
      const stripe = await getStripeClientFromDb();
      const stripeSub = await stripe.subscriptions.retrieve(sub.stripeSubscriptionId);
      const itemId = stripeSub.items.data[0]?.id;
      if (!itemId) {
        return NextResponse.json({ prorationAmount: null, currency: configuredCurrency, lines: [], noSubscription: false });
      }

      const previewItem: Stripe.InvoiceCreatePreviewParams.SubscriptionDetails.Item = { id: itemId, price: fallbackPriceId };
      const preview = await stripe.invoices.createPreview({
        customer: sub.stripeCustomerId,
        subscription: sub.stripeSubscriptionId,
        subscription_details: {
          items: [previewItem],
          proration_behavior: 'always_invoice',
        },
      });

      const prorationLines = preview.lines.data
        .filter((l: Stripe.InvoiceLineItem) => {
          const parent = l.parent as Record<string, unknown> | null;
          return (parent?.subscription_item_details as Record<string, unknown> | undefined)?.proration === true
            || (parent?.invoice_item_details as Record<string, unknown> | undefined)?.proration === true;
        })
        .map((l: Stripe.InvoiceLineItem) => ({
          description: l.description ?? '',
          amount: l.amount / 100,
          currency: (l.currency ?? configuredCurrency.toLowerCase()).toUpperCase(),
        }));

      return NextResponse.json({
        prorationAmount: preview.amount_due / 100,
        currency: (preview.currency ?? configuredCurrency.toLowerCase()).toUpperCase(),
        lines: prorationLines,
        noSubscription: false,
        staticPreview: false,
      });
    } catch (err) {
      log.warn({ err }, 'Proration preview failed, returning static amount');
      return NextResponse.json({
        prorationAmount: effectiveAmount,
        currency: configuredCurrency,
        lines: [{ description: `${plan.name} plan (${billingCycle})`, amount: effectiveAmount, currency: configuredCurrency }],
        noSubscription: false,
        staticPreview: true,
      });
    }
  })
);
