import { type ListingApprovedEvent } from '../../domain/events/listing-approved.event';
import { type ListingRejectedEvent } from '../../domain/events/listing-rejected.event';
import { ModerationAuditListener } from '../listeners/moderation-audit.listener';

describe('ModerationAuditListener', () => {
  let listener: ModerationAuditListener;
  let mockRepo: { create: ReturnType<typeof vi.fn> };
  let mockLogger: {
    log: ReturnType<typeof vi.fn>;
    error: ReturnType<typeof vi.fn>;
  };

  beforeEach(() => {
    mockRepo = {
      create: vi.fn().mockResolvedValue({
        id: 'mod-audit-1',
        targetType: 'listing',
        targetId: 'listing-1',
        action: 'approve',
        moderatorId: 'admin-1',
        reason: null,
        metadata: null,
        createdAt: new Date(),
      }),
    };
    mockLogger = { log: vi.fn(), error: vi.fn() };

    listener = new ModerationAuditListener(
      mockRepo as any,
      mockLogger as any,
    );
  });

  it('writes a moderation audit row when a listing is approved with notes', async () => {
    const event: ListingApprovedEvent = {
      aggregateId: 'listing-1',
      adminId: 'admin-1',
      moderationNotes: 'OK',
      eventName: 'listing.approved_by_admin',
      occurredAt: new Date(),
    };

    await listener.onListingApproved(event);

    expect(mockRepo.create).toHaveBeenCalledWith({
      targetType: 'listing',
      targetId: 'listing-1',
      action: 'approve',
      moderatorId: 'admin-1',
      reason: 'OK',
      metadata: { moderationNotes: 'OK' },
    });
  });

  it('writes a moderation audit row when a listing is approved without notes', async () => {
    const event: ListingApprovedEvent = {
      aggregateId: 'listing-1',
      adminId: 'admin-1',
      eventName: 'listing.approved_by_admin',
      occurredAt: new Date(),
    };

    await listener.onListingApproved(event);

    expect(mockRepo.create).toHaveBeenCalledWith({
      targetType: 'listing',
      targetId: 'listing-1',
      action: 'approve',
      moderatorId: 'admin-1',
      reason: undefined,
      metadata: undefined,
    });
  });

  it('writes a moderation audit row when a listing is rejected', async () => {
    const event: ListingRejectedEvent = {
      aggregateId: 'listing-2',
      adminId: 'admin-2',
      reason: 'Vi phạm nội dung',
      eventName: 'listing.rejected_by_admin',
      occurredAt: new Date(),
    };

    await listener.onListingRejected(event);

    expect(mockRepo.create).toHaveBeenCalledWith({
      targetType: 'listing',
      targetId: 'listing-2',
      action: 'reject',
      moderatorId: 'admin-2',
      reason: 'Vi phạm nội dung',
      metadata: { reason: 'Vi phạm nội dung' },
    });
  });

  it('does not throw when repository write fails', async () => {
    mockRepo.create.mockRejectedValue(new Error('DB down'));
    const event: ListingApprovedEvent = {
      aggregateId: 'listing-3',
      adminId: 'admin-1',
      eventName: 'listing.approved_by_admin',
      occurredAt: new Date(),
    };

    await expect(listener.onListingApproved(event)).resolves.toBeUndefined();

    expect(mockLogger.error).toHaveBeenCalledWith(
      expect.stringContaining('Failed to write moderation audit log'),
      expect.any(String),
      'ModerationAuditListener',
    );
  });

  it('logs success after writing audit entry', async () => {
    const event: ListingRejectedEvent = {
      aggregateId: 'listing-9',
      adminId: 'admin-9',
      reason: 'spam',
      eventName: 'listing.rejected_by_admin',
      occurredAt: new Date(),
    };

    await listener.onListingRejected(event);

    expect(mockLogger.log).toHaveBeenCalledWith(
      'Moderation audit: reject by admin-9 on listing:listing-9',
      'ModerationAuditListener',
    );
  });
});
