import { PaymentEntity } from '../../domain/entities/payment.entity';
import { type IPaymentRepository } from '../../domain/repositories/payment.repository';
import { Money } from '../../domain/value-objects/money.vo';
import { GetPaymentStatusHandler } from '../queries/get-payment-status/get-payment-status.handler';
import { GetPaymentStatusQuery } from '../queries/get-payment-status/get-payment-status.query';

function createPayment(): PaymentEntity {
  const money = Money.create(500_000n).unwrap();
  const payment = PaymentEntity.createNew('pay-1', 'user-1', 'VNPAY', 'SUBSCRIPTION', money);
  payment.markProcessing('vnpay-tx-1');
  payment.clearDomainEvents();
  return payment;
}

describe('GetPaymentStatusHandler', () => {
  let handler: GetPaymentStatusHandler;
  let mockPaymentRepo: { [K in keyof IPaymentRepository]: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockPaymentRepo = {
      findById: vi.fn(),
      findByProviderTxId: vi.fn(),
      findByIdempotencyKey: vi.fn(),
      findByUserId: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
      updateIfStatus: vi.fn(),
    };

    handler = new GetPaymentStatusHandler(mockPaymentRepo as any);
  });

  it('returns payment status for owner', async () => {
    const payment = createPayment();
    mockPaymentRepo.findById.mockResolvedValue(payment);

    const query = new GetPaymentStatusQuery('pay-1', 'user-1');
    const result = await handler.execute(query);

    expect(result.id).toBe('pay-1');
    expect(result.provider).toBe('VNPAY');
    expect(result.status).toBe('PROCESSING');
    expect(result.amountVND).toBe('500000');
    expect(result.providerTxId).toBe('vnpay-tx-1');
  });

  it('throws NotFoundException when payment not found', async () => {
    mockPaymentRepo.findById.mockResolvedValue(null);

    const query = new GetPaymentStatusQuery('nonexistent', 'user-1');

    await expect(handler.execute(query)).rejects.toThrow('Payment');
  });

  it('throws ForbiddenException when user is not owner', async () => {
    const payment = createPayment();
    mockPaymentRepo.findById.mockResolvedValue(payment);

    const query = new GetPaymentStatusQuery('pay-1', 'other-user');

    await expect(handler.execute(query)).rejects.toThrow(/quyền/);
  });
});
