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 seatClass = await prisma.seatClass.upsert({
      where: { name: 'Economy Regular' },
      update: {},
      create: { name: 'Economy Regular', description: 'Standard economy seating', basePrice: 45000, isActive: true },
    });

    const coach = await prisma.coach.create({
      data: { coachNumber: 'TEST-C1', label: 'A', seatClassId: seatClass.id, mode: 'seat', totalUnits: 10 },
    });

    await prisma.coachAssignment.create({ data: { scheduleId: schedule.id, coachId: coach.id, positionNumber: 1 } });

    const seat = await prisma.seat.create({ data: { coachId: coach.id, row: 1, col: 'A', label: '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);
    });
  });

  describe('Webhook endpoints', () => {
    it('should handle Telebirr webhook', async () => {
      await request(app.getHttpServer())
        .post('/payments/webhooks/telebirr')
        .send({ merch_order_id: 'TEST-ORDER-123', payment_order_id: 'PAY-123', trade_status: 'Completed', sign: 'mock-signature' })
        .expect(200);
    });

    it('should handle CBE Birr webhook', async () => {
      await request(app.getHttpServer())
        .post('/payments/webhooks/cbe-birr')
        .send({ merchantId: 'TEST-MERCHANT', merchantOrderId: 'TEST-ORDER-123', orderId: 'CBE-ORDER-123', status: 'SUCCESS', signature: 'mock-signature' })
        .expect(200);
    });

    it('should handle eBirr webhook', async () => {
      await request(app.getHttpServer())
        .post('/payments/webhooks/ebirr')
        .send({ merchantCode: 'TEST-MERCHANT', orderNo: 'TEST-ORDER-123', tradeStatus: 'TRADE_SUCCESS', timestamp: Date.now(), sign: 'mock-signature' })
        .expect(200);
    });

    it('should handle Card webhook', async () => {
      await request(app.getHttpServer())
        .post('/payments/webhooks/card')
        .set('stripe-signature', 'mock-signature')
        .send({ id: 'evt_123', type: 'payment_intent.succeeded', data: { object: { id: 'pi_123', status: 'succeeded', amount: 50000, currency: 'ETB', metadata: { merchantOrderId: 'TEST-ORDER-123', bookingRef: 'TEST-BOOK-001' } } }, created: Math.floor(Date.now() / 1000) })
        .expect(200);
    });
  });
});
