import { PaymentEntity } from '../../domain/entities/payment.entity';
import { type IPaymentRepository } from '../../domain/repositories/payment.repository';
import { Money } from '../../domain/value-objects/money.vo';
import { ListTransactionsHandler } from '../queries/list-transactions/list-transactions.handler';
import { ListTransactionsQuery } from '../queries/list-transactions/list-transactions.query';

function createPayment(id: string, status: 'PENDING' | 'COMPLETED' = 'COMPLETED'): PaymentEntity {
  const money = Money.create(1_000_000n).unwrap();
  const payment = PaymentEntity.createNew(id, 'user-1', 'VNPAY', 'SUBSCRIPTION', money);
  if (status === 'COMPLETED') {
    payment.markProcessing('tx-' + id);
    payment.markCompleted({ verified: true });
  }
  payment.clearDomainEvents();
  return payment;
}

describe('ListTransactionsHandler', () => {
  let handler: ListTransactionsHandler;
  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 ListTransactionsHandler(mockPaymentRepo as any);
  });

  it('returns paginated transactions', async () => {
    const payments = [createPayment('pay-1'), createPayment('pay-2')];
    mockPaymentRepo.findByUserId.mockResolvedValue({ items: payments, total: 2 });

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

    expect(result.items).toHaveLength(2);
    expect(result.total).toBe(2);
    expect(result.items[0].amountVND).toBe('1000000');
    expect(result.limit).toBe(20);
    expect(result.offset).toBe(0);
  });

  it('applies custom limit and offset', async () => {
    mockPaymentRepo.findByUserId.mockResolvedValue({ items: [], total: 0 });

    const query = new ListTransactionsQuery('user-1', undefined, 10, 20);
    await handler.execute(query);

    expect(mockPaymentRepo.findByUserId).toHaveBeenCalledWith('user-1', {
      status: undefined,
      limit: 10,
      offset: 20,
    });
  });

  it('caps limit at 100', async () => {
    mockPaymentRepo.findByUserId.mockResolvedValue({ items: [], total: 0 });

    const query = new ListTransactionsQuery('user-1', undefined, 500);
    const result = await handler.execute(query);

    expect(result.limit).toBe(100);
  });

  it('filters by status', async () => {
    mockPaymentRepo.findByUserId.mockResolvedValue({ items: [], total: 0 });

    const query = new ListTransactionsQuery('user-1', 'COMPLETED');
    await handler.execute(query);

    expect(mockPaymentRepo.findByUserId).toHaveBeenCalledWith('user-1',
      expect.objectContaining({ status: 'COMPLETED' }),
    );
  });
});
