import { ActivateFeaturedListingHandler } from '../event-handlers/activate-featured-listing.handler';

describe('ActivateFeaturedListingHandler', () => {
  let handler: ActivateFeaturedListingHandler;
  let mockPrisma: {
    payment: { findUnique: ReturnType<typeof vi.fn> };
    listing: { findUnique: ReturnType<typeof vi.fn>; update: ReturnType<typeof vi.fn> };
  };
  let mockLogger: { log: ReturnType<typeof vi.fn> };
  let mockEventBus: { publish: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockPrisma = {
      payment: { findUnique: vi.fn() },
      listing: { findUnique: vi.fn(), update: vi.fn() },
    };
    mockLogger = { log: vi.fn() };
    mockEventBus = { publish: vi.fn() };

    handler = new ActivateFeaturedListingHandler(
      mockPrisma as any,
      mockLogger as any,
      mockEventBus as any,
    );
  });

  it('activates featured listing for 7 days on 199000 VND payment', async () => {
    mockPrisma.payment.findUnique.mockResolvedValue({
      type: 'FEATURED_LISTING',
      transactionId: 'listing-1',
      amountVND: 199000n,
    });
    mockPrisma.listing.findUnique.mockResolvedValue({ featuredUntil: null });
    mockPrisma.listing.update.mockResolvedValue({});

    await handler.handle({ aggregateId: 'pay-1' } as any);

    expect(mockPrisma.listing.update).toHaveBeenCalledWith({
      where: { id: 'listing-1' },
      data: { featuredUntil: expect.any(Date), featuredPackage: '7_days' },
    });

    const updateCall = mockPrisma.listing.update.mock.calls[0][0];
    const featuredUntil = updateCall.data.featuredUntil as Date;
    const diffDays = Math.round((featuredUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
    expect(diffDays).toBe(7);
  });

  it('activates featured listing for 3 days on 99000 VND payment', async () => {
    mockPrisma.payment.findUnique.mockResolvedValue({
      type: 'FEATURED_LISTING',
      transactionId: 'listing-1',
      amountVND: 99000n,
    });
    mockPrisma.listing.findUnique.mockResolvedValue({ featuredUntil: null });
    mockPrisma.listing.update.mockResolvedValue({});

    await handler.handle({ aggregateId: 'pay-1' } as any);

    const updateCall = mockPrisma.listing.update.mock.calls[0][0];
    const featuredUntil = updateCall.data.featuredUntil as Date;
    const diffDays = Math.round((featuredUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
    expect(diffDays).toBe(3);
    expect(updateCall.data.featuredPackage).toBe('3_days');
  });

  it('activates featured listing for 30 days on 499000 VND payment', async () => {
    mockPrisma.payment.findUnique.mockResolvedValue({
      type: 'FEATURED_LISTING',
      transactionId: 'listing-1',
      amountVND: 499000n,
    });
    mockPrisma.listing.findUnique.mockResolvedValue({ featuredUntil: null });
    mockPrisma.listing.update.mockResolvedValue({});

    await handler.handle({ aggregateId: 'pay-1' } as any);

    const updateCall = mockPrisma.listing.update.mock.calls[0][0];
    const featuredUntil = updateCall.data.featuredUntil as Date;
    const diffDays = Math.round((featuredUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
    expect(diffDays).toBe(30);
    expect(updateCall.data.featuredPackage).toBe('30_days');
  });

  it('extends from existing featuredUntil if still in the future', async () => {
    const futureDate = new Date(Date.now() + 5 * 24 * 60 * 60 * 1000); // 5 days from now
    mockPrisma.payment.findUnique.mockResolvedValue({
      type: 'FEATURED_LISTING',
      transactionId: 'listing-1',
      amountVND: 199000n,
    });
    mockPrisma.listing.findUnique.mockResolvedValue({ featuredUntil: futureDate });
    mockPrisma.listing.update.mockResolvedValue({});

    await handler.handle({ aggregateId: 'pay-1' } as any);

    const updateCall = mockPrisma.listing.update.mock.calls[0][0];
    const featuredUntil = updateCall.data.featuredUntil as Date;
    // Should extend from futureDate (5 days out) + 7 days = ~12 days from now
    const diffDays = Math.round((featuredUntil.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
    expect(diffDays).toBe(12);
  });

  it('publishes listing.updated event for Typesense re-indexing', async () => {
    mockPrisma.payment.findUnique.mockResolvedValue({
      type: 'FEATURED_LISTING',
      transactionId: 'listing-1',
      amountVND: 199000n,
    });
    mockPrisma.listing.findUnique.mockResolvedValue({ featuredUntil: null });
    mockPrisma.listing.update.mockResolvedValue({});

    await handler.handle({ aggregateId: 'pay-1' } as any);

    expect(mockEventBus.publish).toHaveBeenCalledWith(
      expect.objectContaining({
        eventName: 'listing.updated',
        aggregateId: 'listing-1',
      }),
    );
  });

  it('ignores non-FEATURED_LISTING payments', async () => {
    mockPrisma.payment.findUnique.mockResolvedValue({
      type: 'SUBSCRIPTION',
      transactionId: 'listing-1',
      amountVND: 199000n,
    });

    await handler.handle({ aggregateId: 'pay-1' } as any);

    expect(mockPrisma.listing.update).not.toHaveBeenCalled();
  });

  it('ignores payments without transactionId', async () => {
    mockPrisma.payment.findUnique.mockResolvedValue({
      type: 'FEATURED_LISTING',
      transactionId: null,
      amountVND: 199000n,
    });

    await handler.handle({ aggregateId: 'pay-1' } as any);

    expect(mockPrisma.listing.update).not.toHaveBeenCalled();
  });

  it('ignores payments that do not exist', async () => {
    mockPrisma.payment.findUnique.mockResolvedValue(null);

    await handler.handle({ aggregateId: 'pay-1' } as any);

    expect(mockPrisma.listing.update).not.toHaveBeenCalled();
  });
});
