import { SubscriptionEntity } from '../../domain/entities/subscription.entity';
import { type ISubscriptionRepository } from '../../domain/repositories/subscription.repository';
import { GetBillingHistoryHandler } from '../queries/get-billing-history/get-billing-history.handler';
import { GetBillingHistoryQuery } from '../queries/get-billing-history/get-billing-history.query';

describe('GetBillingHistoryHandler', () => {
  let handler: GetBillingHistoryHandler;
  let mockRepo: { [K in keyof ISubscriptionRepository]: ReturnType<typeof vi.fn> };
  let mockPrisma: any;

  beforeEach(() => {
    mockRepo = {
      findById: vi.fn(),
      findByUserId: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
    };

    mockPrisma = {
      payment: {
        findMany: vi.fn(),
        count: vi.fn(),
      },
    };

    handler = new GetBillingHistoryHandler(mockRepo as any, mockPrisma);
  });

  it('returns billing history with subscription and payments', async () => {
    const sub = SubscriptionEntity.createNew(
      'sub-1', 'user-1', 'plan-1', 'AGENT_PRO',
      new Date('2026-01-01'), new Date('2026-02-01'),
    );
    sub.clearDomainEvents();
    mockRepo.findByUserId.mockResolvedValue(sub);

    mockPrisma.payment.findMany.mockResolvedValue([
      {
        id: 'pay-1',
        amountVND: 500000n,
        status: 'COMPLETED',
        provider: 'VNPAY',
        createdAt: new Date('2026-01-01'),
      },
    ]);
    mockPrisma.payment.count.mockResolvedValue(1);

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

    expect(result.subscription).not.toBeNull();
    expect(result.subscription!.id).toBe('sub-1');
    expect(result.subscription!.planTier).toBe('AGENT_PRO');
    expect(result.payments).toHaveLength(1);
    expect(result.payments[0].amountVND).toBe('500000');
    expect(result.total).toBe(1);
  });

  it('returns null subscription when user has none', async () => {
    mockRepo.findByUserId.mockResolvedValue(null);
    mockPrisma.payment.findMany.mockResolvedValue([]);
    mockPrisma.payment.count.mockResolvedValue(0);

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

    expect(result.subscription).toBeNull();
    expect(result.payments).toHaveLength(0);
    expect(result.total).toBe(0);
  });

  it('applies limit and offset', async () => {
    mockRepo.findByUserId.mockResolvedValue(null);
    mockPrisma.payment.findMany.mockResolvedValue([]);
    mockPrisma.payment.count.mockResolvedValue(0);

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

    expect(mockPrisma.payment.findMany).toHaveBeenCalledWith(
      expect.objectContaining({ take: 10, skip: 20 }),
    );
  });
});
