import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import { Webhook } from 'standardwebhooks';
import { POST } from '@/app/api/webhooks/dodo/route';
import { prisma } from '@/lib/db';
import { resetDb, mkUser, mkOrder } from './helpers';

const SECRET = 'whsec_' + Buffer.from('dodo-test-secret-1234567890').toString('base64');
beforeAll(() => { process.env.DODO_WEBHOOK_SECRET = SECRET; });
beforeEach(resetDb);

function signed(payload: object, id = 'msg_' + Math.random().toString(36).slice(2)) {
  const body = JSON.stringify(payload); const ts = new Date();
  const sig = new Webhook(SECRET).sign(id, ts, body);
  return new Request('http://x/api/webhooks/dodo', { method: 'POST', body, headers: { 'webhook-id': id, 'webhook-timestamp': String(Math.floor(ts.getTime() / 1000)), 'webhook-signature': sig, 'content-type': 'application/json' } });
}

describe('Dodo webhook', () => {
  it('geçersiz imzayı 401 ile reddeder', async () => {
    const r = await POST(new Request('http://x', { method: 'POST', body: '{}', headers: { 'webhook-id': 'a', 'webhook-timestamp': '1', 'webhook-signature': 'v1,bad' } }));
    expect(r.status).toBe(401);
  });

  it('payment.succeeded → kredi yükler, siparişi kuyruğa alır', async () => {
    const u = await mkUser(); const o = await mkOrder(u.id, { plan: 'paket' });
    await prisma.payment.create({ data: { userId: u.id, orderId: o.id, plan: 'paket', credits: 5, amount: 499000, dodoSessionId: 'cks_1' } });
    const r = await POST(signed({ type: 'payment.succeeded', data: { payment_id: 'pay_1', total_amount: 499000, currency: 'TRY', metadata: { orderId: o.id, userId: u.id, plan: 'paket' } } }));
    expect(r.status).toBe(200);
    const [u2, o2, jobs, pay] = await Promise.all([prisma.user.findUnique({ where: { id: u.id } }), prisma.order.findUnique({ where: { id: o.id } }), prisma.job.findMany({ where: { orderId: o.id } }), prisma.payment.findFirst({ where: { orderId: o.id } })]);
    expect(u2!.credits).toBe(4); // 5 kredi - 1 bu sipariş
    expect(o2!.status).toBe('QUEUED');
    expect(jobs).toHaveLength(1);
    expect(pay!.status).toBe('succeeded');
    expect(pay!.dodoPaymentId).toBe('pay_1');
  });

  it('aynı webhook-id tekrar gelirse işlem tekrarlanmaz (idempotent)', async () => {
    const u = await mkUser(); const o = await mkOrder(u.id);
    const payload = { type: 'payment.succeeded', data: { payment_id: 'pay_2', metadata: { orderId: o.id, userId: u.id, plan: 'tek' } } };
    await POST(signed(payload, 'msg_same'));
    const r2 = await POST(signed(payload, 'msg_same'));
    expect((await r2.json()).duplicate).toBe(true);
    expect((await prisma.user.findUnique({ where: { id: u.id } }))!.credits).toBe(0);
    expect(await prisma.job.count({ where: { orderId: o.id } })).toBe(1);
  });

  it('farklı webhook-id ile aynı ödeme tekrar gelirse çift kredi verilmez', async () => {
    const u = await mkUser();
    const payload = { type: 'payment.succeeded', data: { payment_id: 'pay_3', metadata: { userId: u.id, plan: 'paket' } } };
    await POST(signed(payload)); await POST(signed(payload));
    expect((await prisma.user.findUnique({ where: { id: u.id } }))!.credits).toBe(5);
  });

  it('işlem yarıda kalırsa olay kaydı geri alınır, Dodo aynı webhook-id ile yeniden denediğinde işlenir', async () => {
    const payload = { type: 'payment.succeeded', data: { payment_id: 'pay_5', metadata: { userId: 'kullanici_henuz_yok', plan: 'paket' } } };
    const r1 = await POST(signed(payload, 'msg_retry'));
    expect(r1.status).toBe(500);
    expect(await prisma.webhookEvent.count({ where: { id: 'msg_retry' } })).toBe(0);
    await prisma.user.create({ data: { id: 'kullanici_henuz_yok', email: 'retry@test.tr', name: 'Retry', passwordHash: 'x', emailVerifiedAt: new Date() } });
    const r2 = await POST(signed(payload, 'msg_retry'));
    expect(r2.status).toBe(200);
    expect((await r2.json()).duplicate).toBeUndefined();
    expect((await prisma.user.findUnique({ where: { id: 'kullanici_henuz_yok' } }))!.credits).toBe(5);
  });

  it('sipariş arada krediyle ödenip teslim edildiyse gelen ödeme siparişi geri sarmaz, paketin tamamı krediye eklenir', async () => {
    const u = await mkUser(); const o = await mkOrder(u.id, { plan: 'paket', status: 'DELIVERED' });
    await prisma.payment.create({ data: { userId: u.id, orderId: o.id, plan: 'paket', credits: 5, amount: 499000, dodoSessionId: 'cks_late' } });
    const r = await POST(signed({ type: 'payment.succeeded', data: { payment_id: 'pay_6', checkout_session_id: 'cks_late', metadata: { orderId: o.id, userId: u.id, plan: 'paket' } } }));
    expect(r.status).toBe(200);
    expect((await prisma.order.findUnique({ where: { id: o.id } }))!.status).toBe('DELIVERED');
    expect(await prisma.job.count({ where: { orderId: o.id } })).toBe(0);
    expect((await prisma.user.findUnique({ where: { id: u.id } }))!.credits).toBe(5);
  });

  it('aynı sipariş için ikinci checkout da ödenirse yeniden render başlamaz', async () => {
    const u = await mkUser(); const o = await mkOrder(u.id, { plan: 'paket' });
    for (const s of ['cks_a', 'cks_b']) await prisma.payment.create({ data: { userId: u.id, orderId: o.id, plan: 'paket', credits: 5, amount: 499000, dodoSessionId: s } });
    const md = { orderId: o.id, userId: u.id, plan: 'paket' };
    await POST(signed({ type: 'payment.succeeded', data: { payment_id: 'pay_a', checkout_session_id: 'cks_a', metadata: md } }));
    await prisma.order.update({ where: { id: o.id }, data: { status: 'RENDERING' } });
    await POST(signed({ type: 'payment.succeeded', data: { payment_id: 'pay_b', checkout_session_id: 'cks_b', metadata: md } }));
    expect((await prisma.order.findUnique({ where: { id: o.id } }))!.status).toBe('RENDERING');
    expect(await prisma.job.count({ where: { orderId: o.id } })).toBe(1);
    expect((await prisma.user.findUnique({ where: { id: u.id } }))!.credits).toBe(4 + 5);
    expect(await prisma.payment.count({ where: { orderId: o.id, status: 'succeeded' } })).toBe(2);
  });

  it('payment.failed bekleyen ödemeyi başarısız işaretler', async () => {
    const u = await mkUser(); const o = await mkOrder(u.id);
    await prisma.payment.create({ data: { userId: u.id, orderId: o.id, plan: 'tek', credits: 1, amount: 129000 } });
    await POST(signed({ type: 'payment.failed', data: { payment_id: 'pay_4', metadata: { orderId: o.id, userId: u.id, plan: 'tek' } } }));
    expect((await prisma.payment.findFirst({ where: { orderId: o.id } }))!.status).toBe('failed');
    expect((await prisma.order.findUnique({ where: { id: o.id } }))!.status).toBe('PENDING_PAYMENT');
  });
});
