import { NextResponse } from 'next/server';
import { createHmac } from 'crypto';
import { db } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import { getRazorpayCredentials } from '@server/lib/razorpay';
import { childLogger } from '@server/logger';
import { wrapRouteHandler } from '@server/logger/request';

const log = childLogger('webhook.razorpay');

export const runtime = 'nodejs';

type RazorpayStatus = 'active' | 'trial' | 'past_due' | 'cancelled' | 'inactive';

function normalizeRazorpayStatus(event: string): RazorpayStatus | null {
  switch (event) {
    case 'subscription.activated':
    case 'subscription.charged':
      return 'active';
    case 'subscription.halted':
    case 'subscription.pending':
    case 'payment.failed':
      return 'past_due';
    case 'subscription.cancelled':
    case 'subscription.completed':
      return 'cancelled';
    case 'subscription.paused':
      return 'inactive';
    default:
      return null;
  }
}

async function handleRazorpayWebhook(req: Request): Promise<NextResponse> {
  await initDatabase();

  const body = await req.text();
  const sig = req.headers.get('x-razorpay-signature');
  const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET;

  if (!webhookSecret) {
    log.error('RAZORPAY_WEBHOOK_SECRET is not configured — rejecting request');
    return NextResponse.json({ error: 'Webhook secret not configured' }, { status: 500 });
  }

  const expectedSig = createHmac('sha256', webhookSecret).update(body).digest('hex');
  if (expectedSig !== sig) {
    log.warn({ signatureValid: false }, 'webhook.razorpay: signature mismatch');
    return NextResponse.json({ error: 'Webhook signature verification failed' }, { status: 400 });
  }

  let event: { event: string; payload?: Record<string, unknown> };
  try {
    event = JSON.parse(body) as typeof event;
  } catch {
    return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 });
  }

  log.info({ event: event.event }, 'webhook.razorpay: received');

  const creds = await getRazorpayCredentials();

  type SubEntity = {
    id?: string;
    plan_id?: string;
    status?: string;
    current_start?: number;
    current_end?: number;
    notes?: { restaurant_id?: string; plan_id?: string };
  };

  const subEntity = (
    (event.payload?.subscription as { entity?: SubEntity } | undefined)?.entity
  ) as SubEntity | undefined;

  if (!subEntity?.id) {
    return NextResponse.json({ received: true });
  }

  const subscriptionId = subEntity.id;
  let restaurantId: string | null = subEntity.notes?.restaurant_id ?? null;
  let planId: string | null = subEntity.notes?.plan_id ?? null;
  const currentEnd = subEntity.current_end ?? null;

  if (!restaurantId || !planId) {
    try {
      if (!creds) throw new Error('no credentials');
      const { getRazorpayClient } = await import('@server/lib/razorpay');
      const rzp = await getRazorpayClient();
      if (rzp) {
        const fetched = await (rzp.subscriptions.fetch as (id: string) => Promise<SubEntity>)(subscriptionId);
        restaurantId = fetched.notes?.restaurant_id ?? restaurantId;
        planId = fetched.notes?.plan_id ?? planId;
      }
    } catch (err) {
      log.warn({ err, subscriptionId }, 'webhook.razorpay: could not fetch subscription notes');
    }
  }

  if (!restaurantId) {
    const { rows } = await db.execute(sql`
      SELECT restaurant_id FROM public.subscriptions WHERE razorpay_subscription_id = ${subscriptionId} LIMIT 1
    `);
    restaurantId = (rows[0] as { restaurant_id: string | null } | undefined)?.restaurant_id ?? null;
  }

  if (!restaurantId) {
    log.warn({ subscriptionId }, 'webhook.razorpay: cannot resolve restaurant_id — skipping update');
    return NextResponse.json({ received: true });
  }

  const newStatus = normalizeRazorpayStatus(event.event);

  if (!newStatus) {
    log.info({ event: event.event, subscriptionId }, 'webhook.razorpay: unrecognized event — no status update');
    return NextResponse.json({ received: true });
  }

  try {
    if (currentEnd) {
      await db.execute(sql`
        UPDATE public.subscriptions
        SET status = ${newStatus},
            razorpay_subscription_id = ${subscriptionId},
            current_period_end = to_timestamp(${currentEnd})
            ${planId ? sql`, plan_id = ${planId}` : sql``}
        WHERE restaurant_id = ${restaurantId}
      `);
    } else {
      await db.execute(sql`
        UPDATE public.subscriptions
        SET status = ${newStatus},
            razorpay_subscription_id = ${subscriptionId}
            ${planId ? sql`, plan_id = ${planId}` : sql``}
        WHERE restaurant_id = ${restaurantId}
      `);
    }
    log.info({ restaurantId, subscriptionId, event: event.event, newStatus }, 'webhook.razorpay: subscription updated');
  } catch (err) {
    log.error({ err, restaurantId, subscriptionId }, 'webhook.razorpay: failed to update subscription');
    return NextResponse.json({ error: 'Failed to update subscription' }, { status: 500 });
  }

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

export const POST = wrapRouteHandler(handleRazorpayWebhook);
