import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication, ValidationPipe } from "@nestjs/common";
import request from "supertest";
import { AppModule } from "../../app.module";
import { PrismaService } from "../../common/prisma.service";

describe("Payments E2E", () => {
  let app: INestApplication;
  let prisma: PrismaService;
  let authToken: string;
  let bookingId: string;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    app.useGlobalPipes(
      new ValidationPipe({ transform: true, whitelist: true }),
    );
    await app.init();

    prisma = app.get<PrismaService>(PrismaService);

    const testUser = await prisma.user.create({
      data: {
        email: "payment-test@example.com",
        phone: "+251911111112",
        fullName: "Payment Test User",
        passwordHash: "$2b$10$abcdefghijklmnopqrstuvwxyz",
        role: "PASSENGER",
      },
    });

    const passenger = await prisma.passenger.create({
      data: { userId: testUser.id },
    });

    await prisma.walletAccount.create({
      data: {
        passengerId: passenger.id,
        balanceMinor: 100000,
        currency: "ETB",
      },
    });

    authToken = "mock-jwt-token";

    const station1 = await prisma.station.create({
      data: {
        code: "TST1",
        name: "Test Station 1",
        city: "Test City",
        lat: 9.0,
        lng: 38.0,
      },
    });
    const station2 = await prisma.station.create({
      data: {
        code: "TST2",
        name: "Test Station 2",
        city: "Test City 2",
        lat: 9.5,
        lng: 38.5,
      },
    });

    const train = await prisma.train.create({
      data: { number: "TEST-001", name: "Test Train" },
    });

    const schedule = await prisma.trainSchedule.create({
      data: {
        trainId: train.id,
        originStationId: station1.id,
        destinationStationId: station2.id,
        departureAt: new Date(Date.now() + 86400000),
        arrivalAt: new Date(Date.now() + 90000000),
        durationMinutes: 60,
      },
    });

    const coachType = await prisma.coachType.create({
      data: { name: "Standard", code: "STD" },
    });

    const seatClass = await prisma.seatClass.create({
      data: {
        name: "Economy Regular",
        description: "Standard economy seating",
        baseFareMinor: 45000,
        isActive: true,
        coachTypeId: coachType.id,
      },
    });

    const coach = await prisma.coach.create({
      data: {
        coachTypeId: coachType.id,
        number: "TEST-C1",
        arrangement: "2+2",
        capacity: 10,
        status: "ACTIVE",
      },
    });

    const seat = await prisma.seat.create({
      data: {
        coachId: coach.id,
        row: 1,
        col: "A",
        seatNumber: "1A",
        status: "AVAILABLE",
      },
    });

    const booking = await prisma.booking.create({
      data: {
        bookingRef: "TEST-BOOK-001",
        passengerId: passenger.id,
        scheduleId: schedule.id,
        status: "PENDING_PAYMENT",
        totalMinor: 50000,
        currency: "ETB",
      },
    });

    await prisma.bookingSeat.create({
      data: {
        bookingId: booking.id,
        seatId: seat.id,
        passengerName: "Test Passenger",
      },
    });

    bookingId = booking.id;
  });

  afterAll(async () => {
    await prisma.$transaction([
      prisma.bookingSeat.deleteMany(),
      prisma.paymentIntent.deleteMany(),
      prisma.booking.deleteMany(),
      prisma.coachAssignment.deleteMany(),
      prisma.seat.deleteMany(),
      prisma.coach.deleteMany(),
      prisma.trainSchedule.deleteMany(),
      prisma.train.deleteMany(),
      prisma.station.deleteMany({ where: { code: { in: ["TST1", "TST2"] } } }),
      prisma.walletLedgerEntry.deleteMany(),
      prisma.walletAccount.deleteMany(),
      prisma.passenger.deleteMany(),
      prisma.user.deleteMany({ where: { email: "payment-test@example.com" } }),
    ]);
    await app.close();
  });

  describe("POST /payments/initiate", () => {
    it("should initiate wallet payment successfully", async () => {
      const response = await request(app.getHttpServer())
        .post("/payments/initiate")
        .set("Authorization", `Bearer ${authToken}`)
        .send({ bookingId, method: "WALLET" })
        .expect(201);
      expect(response.body.intentId).toBeDefined();
      expect(response.body.status).toBe("SUCCEEDED");
    });

    it("should return 400 for invalid payment method", async () => {
      await request(app.getHttpServer())
        .post("/payments/initiate")
        .set("Authorization", `Bearer ${authToken}`)
        .send({ bookingId, method: "INVALID_METHOD" })
        .expect(400);
    });

    it("should return 404 for non-existent booking", async () => {
      await request(app.getHttpServer())
        .post("/payments/initiate")
        .set("Authorization", `Bearer ${authToken}`)
        .send({ bookingId: "non-existent-id", method: "WALLET" })
        .expect(404);
    });
  });

  describe("GET /payments/intents/:bookingId", () => {
    it("should get payment intent status", async () => {
      const response = await request(app.getHttpServer())
        .get(`/payments/intents/${bookingId}`)
        .set("Authorization", `Bearer ${authToken}`)
        .expect(200);
      expect(response.body.intentId).toBeDefined();
      expect(response.body.status).toBeDefined();
    });

    it("should return 404 for non-existent intent", async () => {
      await request(app.getHttpServer())
        .get("/payments/intents/non-existent-booking")
        .set("Authorization", `Bearer ${authToken}`)
        .expect(404);
    });
  });

  // Provider webhooks moved to the payment microservice (apps/edr-payment-api /webhooks/*).
});
