import type { Prisma } from '@prisma/client';
import { prisma } from '@/lib/db';
import { verifyDodoWebhook } from '@/lib/dodo';
import { notifyQueued, payWithCredit, queueOrderTx } from '@/lib/orders';
import { planById, type Plan } from '@/lib/plans';
import { sendEmail } from '@/lib/notify';
import { log } from '@/lib/http';

export const dynamic = 'force-dynamic';

type Evt = ReturnType<typeof verifyDodoWebhook>;
type Result = { plan?: Plan; queued?: boolean; ignored?: string };

/** Ödeme kaydı + kredi + (bekliyorsa) siparişin kapatılması; tek transaction içinde. */
async function paymentSucceeded(tx: Prisma.TransactionClient, d: Evt['data'], md: Record<string, string>): Promise<Result> {
  const plan = planById(md.plan || '');
  if (!md.userId || !plan) return { ignored: 'metadata' };
  const or: object[] = [];
  if (d.payment_id) or.push({ dodoPaymentId: d.payment_id });
  if (d.checkout_session_id) or.push({ dodoSessionId: d.checkout_session_id });
  if (md.orderId) or.push({ orderId: md.orderId, userId: md.userId, status: 'pending' });
  const pay = or.length ? await tx.payment.findFirst({ where: { OR: or } }) : null;
  if (pay?.status === 'succeeded') return {};
  if (pay) await tx.payment.update({ where: { id: pay.id }, data: { status: 'succeeded', dodoPaymentId: d.payment_id, amount: d.total_amount ?? pay.amount, currency: d.currency ?? pay.currency } });
  else await tx.payment.create({ data: { userId: md.userId, orderId: md.orderId || null, plan: plan.id, credits: plan.credits, amount: d.total_amount ?? plan.price * 100, currency: d.currency ?? 'TRY', status: 'succeeded', dodoPaymentId: d.payment_id } });
  // Sipariş yalnızca hâlâ ödeme bekliyorsa bu ödemeyle kapanır ve 1 kredi tüketir. Arada kredisiyle ödenmiş/üretilmiş
  // bir siparişe dokunulmaz (geri sarma, yeniden render yok); paketin tamamı krediye eklenir.
  const orderPaid = !!md.orderId && (await tx.order.updateMany({ where: { id: md.orderId, userId: md.userId, status: 'PENDING_PAYMENT' }, data: { status: 'PAID' } })).count > 0;
  await tx.user.update({ where: { id: md.userId }, data: { credits: { increment: plan.credits - (orderPaid ? 1 : 0) } } });
  // Referans: davet edilen kullanıcının ilk ödemesinde davet edene 1 video kredisi (bir kez)
  const buyer = await tx.user.findUnique({ where: { id: md.userId }, select: { referredById: true, refRewardedAt: true } });
  if (buyer?.referredById && !buyer.refRewardedAt) {
    await tx.user.update({ where: { id: md.userId }, data: { refRewardedAt: new Date() } });
    await tx.user.updateMany({ where: { id: buyer.referredById }, data: { credits: { increment: 1 } } });
  }
  return { plan, queued: orderPaid && await queueOrderTx(tx, md.orderId) };
}

export async function POST(req: Request) {
  const raw = await req.text();
  let evt: Evt;
  try { evt = verifyDodoWebhook(raw, req.headers); } catch { return new Response('invalid signature', { status: 401 }); }
  const wid = req.headers.get('webhook-id')!;
  const seen = () => prisma.webhookEvent.findUnique({ where: { id: wid }, select: { id: true } });
  if (await seen()) return Response.json({ ok: true, duplicate: true });

  const d: Evt['data'] = evt.data || {};
  const md = (d.metadata || {}) as Record<string, string>;
  let r: Result;
  try {
    // Idempotency kaydı işlemle aynı transaction'da: işlem yarıda kalırsa kayıt da geri alınır, Dodo'nun yeniden denemesi işlenir.
    r = await prisma.$transaction(async (tx) => {
      await tx.webhookEvent.create({ data: { id: wid, source: 'dodo', type: evt.type, payload: evt as object } });
      if (evt.type === 'payment.succeeded') return paymentSucceeded(tx, d, md);
      if ((evt.type === 'payment.failed' || evt.type === 'payment.cancelled') && md.orderId)
        await tx.payment.updateMany({ where: { orderId: md.orderId, status: 'pending' }, data: { status: 'failed', dodoPaymentId: d.payment_id } });
      return {};
    });
  } catch (e) {
    // aynı teslimat eşzamanlı işlendiyse bu kopyadır
    if (await seen()) return Response.json({ ok: true, duplicate: true });
    log('error', 'dodo_webhook_failed', { webhookId: wid, type: evt.type, err: (e as Error).message });
    return Response.json({ error: 'işlenemedi' }, { status: 500 });
  }
  if (r.ignored) return Response.json({ ok: true, ignored: r.ignored });
  const plan = r.plan;
  if (!plan) return Response.json({ ok: true });

  // Ödeme kaydedildi; bundan sonrası en iyi çaba (hata Dodo'ya 500 olarak dönmez, aksi halde yeniden deneme kopya sayılır).
  try {
    if (r.queued) await notifyQueued(md.orderId);
    if (md.orderId) {
      // Toplu sipariş: aynı gruptaki bekleyen parseller, yüklenen krediden sırayla başlatılır.
      const o = await prisma.order.findUnique({ where: { id: md.orderId }, select: { batchId: true } });
      if (o?.batchId) for (const b of await prisma.order.findMany({ where: { batchId: o.batchId, userId: md.userId, status: 'PENDING_PAYMENT' }, orderBy: { createdAt: 'asc' } })) if (!await payWithCredit(md.userId, b.id)) break;
    }
    const user = await prisma.user.findUnique({ where: { id: md.userId } });
    if (user) await sendEmail(user.email, 'Ödemeniz alındı, videonuz üretimde', `<p>Merhaba ${user.name},</p><p>${plan.name} ödemeniz alındı. Videonuz hazır olduğunda size haber vereceğiz.</p>`);
  } catch (e) { log('error', 'dodo_webhook_followup_failed', { webhookId: wid, err: (e as Error).message }); }
  return Response.json({ ok: true });
}
