import { INestApplication } from "@nestjs/common";
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
import request from "supertest";
import { PrismaService } from "../src/prisma/prisma.service";
import { EncryptionService } from "../src/common/crypto/encryption.service";
import { truncateAll } from "./helpers/db";
import { seedMerchantWithApiKey, type SeededMerchant } from "./helpers/seed";
import { buildTestApp, type TestAppHandle } from "./helpers/test-app";

describe("Payment Intent (e2e)", () => {
  let handle: TestAppHandle;
  let app: INestApplication;
  let prisma: PrismaService;
  let encryption: EncryptionService;
  let seeded: SeededMerchant;

  beforeAll(async () => {
    handle = await buildTestApp();
    app = handle.app;
    prisma = app.get(PrismaService);
    encryption = app.get(EncryptionService);
  });

  afterAll(async () => {
    await handle.close();
  });

  beforeEach(async () => {
    await truncateAll(prisma);
    handle.fakeRouter.reset();
    seeded = await seedMerchantWithApiKey(prisma, encryption);
    const cfg = await prisma.merchantProviderConfig.findUniqueOrThrow({
      where: { id: seeded.providerConfigId },
    });
    handle.fakeRouter.registerConfig(cfg);
  });

  it("rejects requests without bearer token", async () => {
    await request(app.getHttpServer()).post("/payment-intents").send({}).expect(401);
  });

  it("creates a payment intent in 'created' status", async () => {
    const res = await request(app.getHttpServer())
      .post("/payment-intents")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ amount: 150000, currency: "PYG", country: "PY" })
      .expect(201);

    expect(res.body.id).toMatch(/^pi_/);
    expect(res.body.status).toBe("created");
    expect(res.body.currency).toBe("PYG");
  });

  it("idempotency-key returns the same intent on retry", async () => {
    const key = "idem-e2e-1";
    const body = { amount: 5000, currency: "USD", country: "PY" };
    const first = await request(app.getHttpServer())
      .post("/payment-intents")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .set("Idempotency-Key", key)
      .send(body)
      .expect(201);
    const second = await request(app.getHttpServer())
      .post("/payment-intents")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .set("Idempotency-Key", key)
      .send(body)
      .expect(201);
    expect(second.body.id).toBe(first.body.id);
  });

  it("confirm() routes to provider and transitions intent → succeeded on approved", async () => {
    handle.fakeRouter.program({ createStatus: "approved" });
    const created = await request(app.getHttpServer())
      .post("/payment-intents")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ amount: 100000, currency: "PYG", country: "PY" })
      .expect(201);

    const res = await request(app.getHttpServer())
      .post(`/payment-intents/${created.body.id}/confirm`)
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({})
      .expect(201);

    expect(res.body.intent.status).toBe("approved");
    expect(res.body.attempt.id).toMatch(/^pa_/);
    expect(res.body.attempt.providerPaymentId).toMatch(/^fake_pay_/);

    const events = await prisma.webhookEventOut.count();
    expect(events).toBeGreaterThanOrEqual(0); // no endpoints subscribed in seed
  });

  it("confirm() with pending provider response keeps intent in 'pending'", async () => {
    handle.fakeRouter.program({ createStatus: "pending", redirectUrl: "https://fake.checkout/abc" });
    const created = await request(app.getHttpServer())
      .post("/payment-intents")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ amount: 100000, currency: "PYG", country: "PY" })
      .expect(201);

    const res = await request(app.getHttpServer())
      .post(`/payment-intents/${created.body.id}/confirm`)
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({})
      .expect(201);

    expect(res.body.intent.status).toBe("pending");
    expect(res.body.attempt.redirectUrl).toBe("https://fake.checkout/abc");
  });
});
