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, type SeededMerchant } from "./helpers/seed";
import { buildTestApp, type TestAppHandle } from "./helpers/test-app";

async function createApprovedIntent(
  app: INestApplication,
  bearer: string,
  amount: number,
): Promise<{ intentId: string }> {
  const created = await request(app.getHttpServer())
    .post("/payment-intents")
    .set("Authorization", `Bearer ${bearer}`)
    .send({ amount, currency: "PYG", country: "PY" })
    .expect(201);
  await request(app.getHttpServer())
    .post(`/payment-intents/${created.body.id}/confirm`)
    .set("Authorization", `Bearer ${bearer}`)
    .send({})
    .expect(201);
  return { intentId: created.body.id };
}

describe("Refunds (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);
    handle.fakeRouter.program({ createStatus: "approved", refundStatus: "approved" });
  });

  it("full refund transitions the intent to 'refunded'", async () => {
    const { intentId } = await createApprovedIntent(app, seeded.apiKey, 100000);

    const res = await request(app.getHttpServer())
      .post("/refunds")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ intentId })
      .expect(201);

    expect(res.body.status).toBe("approved");
    expect(res.body.id).toMatch(/^re_/);

    const intent = await prisma.paymentIntent.findUniqueOrThrow({ where: { id: intentId } });
    expect(intent.status).toBe("refunded");
  });

  it("partial refund leaves the intent in 'partially_refunded' and computes remaining", async () => {
    const { intentId } = await createApprovedIntent(app, seeded.apiKey, 100000);

    const first = await request(app.getHttpServer())
      .post("/refunds")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ intentId, amount: 40000 })
      .expect(201);
    expect(first.body.status).toBe("approved");

    let intent = await prisma.paymentIntent.findUniqueOrThrow({ where: { id: intentId } });
    expect(intent.status).toBe("partially_refunded");

    const second = await request(app.getHttpServer())
      .post("/refunds")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ intentId, amount: 60000 })
      .expect(201);
    expect(second.body.status).toBe("approved");

    intent = await prisma.paymentIntent.findUniqueOrThrow({ where: { id: intentId } });
    expect(intent.status).toBe("refunded");
  });

  it("rejects refund amount exceeding remaining captured", async () => {
    const { intentId } = await createApprovedIntent(app, seeded.apiKey, 50000);

    await request(app.getHttpServer())
      .post("/refunds")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ intentId, amount: 999999 })
      .expect(400);
  });

  it("rejects refund on intent without an approved attempt", async () => {
    const created = await request(app.getHttpServer())
      .post("/payment-intents")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ amount: 10000, currency: "PYG", country: "PY" })
      .expect(201);

    await request(app.getHttpServer())
      .post("/refunds")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ intentId: created.body.id })
      .expect(400);
  });

  it("404 when intentId does not exist for this merchant", async () => {
    await request(app.getHttpServer())
      .post("/refunds")
      .set("Authorization", `Bearer ${seeded.apiKey}`)
      .send({ intentId: "pi_does_not_exist" })
      .expect(404);
  });
});
