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

describe("Scope guard + Idempotency (e2e)", () => {
  let handle: TestAppHandle;
  let app: INestApplication;
  let prisma: PrismaService;
  let encryption: EncryptionService;

  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();
  });

  describe("Idempotency-Key", () => {
    it("same key + same body returns the same intent", async () => {
      const seeded = await seedMerchantWithApiKey(prisma, encryption);
      const body = { amount: 12345, currency: "USD", country: "PY" };
      const a = await request(app.getHttpServer())
        .post("/payment-intents")
        .set("Authorization", `Bearer ${seeded.apiKey}`)
        .set("Idempotency-Key", "key-1")
        .send(body)
        .expect(201);
      const b = await request(app.getHttpServer())
        .post("/payment-intents")
        .set("Authorization", `Bearer ${seeded.apiKey}`)
        .set("Idempotency-Key", "key-1")
        .send(body)
        .expect(201);
      expect(b.body.id).toBe(a.body.id);

      const count = await prisma.paymentIntent.count();
      expect(count).toBe(1);
    });

    it("same key across different merchants does NOT collide", async () => {
      const m1 = await seedMerchantWithApiKey(prisma, encryption);
      const m2 = await seedMerchantWithApiKey(prisma, encryption);
      const body = { amount: 5000, currency: "PYG", country: "PY" };

      const a = await request(app.getHttpServer())
        .post("/payment-intents")
        .set("Authorization", `Bearer ${m1.apiKey}`)
        .set("Idempotency-Key", "same-key")
        .send(body)
        .expect(201);
      const b = await request(app.getHttpServer())
        .post("/payment-intents")
        .set("Authorization", `Bearer ${m2.apiKey}`)
        .set("Idempotency-Key", "same-key")
        .send(body)
        .expect(201);

      expect(b.body.id).not.toBe(a.body.id);
      expect(b.body.merchantId).toBe(m2.merchantId);
    });
  });

  describe("RequireFullScopeGuard", () => {
    it("restricted scope cannot create webhook endpoints (403)", async () => {
      const restricted = await seedMerchantWithApiKey(prisma, encryption, {
        scope: "restricted",
      });

      await request(app.getHttpServer())
        .post("/webhook-endpoints")
        .set("Authorization", `Bearer ${restricted.apiKey}`)
        .send({ url: "http://x", subscribedEvents: ["*"] })
        .expect(403);
    });

    it("restricted scope cannot create provider configs (403)", async () => {
      const restricted = await seedMerchantWithApiKey(prisma, encryption, {
        scope: "restricted",
      });

      await request(app.getHttpServer())
        .post("/provider-configs")
        .set("Authorization", `Bearer ${restricted.apiKey}`)
        .send({
          provider: "dlocal",
          country: "PY",
          mode: "sandbox",
          credentials: { xLogin: "x", xTransKey: "y", secretKey: "z" },
        })
        .expect(403);
    });

    it("full scope can create webhook endpoints (201)", async () => {
      const full = await seedMerchantWithApiKey(prisma, encryption);

      await request(app.getHttpServer())
        .post("/webhook-endpoints")
        .set("Authorization", `Bearer ${full.apiKey}`)
        .send({ url: "http://127.0.0.1:1/hook", subscribedEvents: ["*"] })
        .expect(201);
    });

    it("restricted scope CAN still create payment intents (201)", async () => {
      const restricted = await seedMerchantWithApiKey(prisma, encryption, {
        scope: "restricted",
      });

      await request(app.getHttpServer())
        .post("/payment-intents")
        .set("Authorization", `Bearer ${restricted.apiKey}`)
        .send({ amount: 1000, currency: "PYG", country: "PY" })
        .expect(201);
    });
  });
});
