import type { CommandBus } from '@nestjs/cqrs';
import type { ModerateListingCommand } from '@modules/listings/application/commands/moderate-listing/moderate-listing.command';
import { ListingCreatedEvent } from '@modules/listings/domain/events/listing-created.event';
import { type IAiServiceClient } from '../../infrastructure/services/ai-service.client';
import { ListingCreatedModerationHandler } from '../event-handlers/listing-created-moderation.handler';

describe('ListingCreatedModerationHandler', () => {
  let handler: ListingCreatedModerationHandler;
  let mockAiClient: IAiServiceClient;
  let mockCommandBus: CommandBus;
  let mockPrisma: { property: { findUnique: ReturnType<typeof vi.fn> } };

  const event = new ListingCreatedEvent('listing-1', 'property-1', 'seller-1', 'SALE');

  beforeEach(() => {
    mockAiClient = {
      predict: vi.fn(),
      moderate: vi.fn(),
      isAvailable: vi.fn(),
    };

    mockCommandBus = {
      execute: vi.fn().mockResolvedValue({ status: 'DRAFT' }),
    } as unknown as CommandBus;

    mockPrisma = {
      property: {
        findUnique: vi.fn().mockResolvedValue({
          title: 'Bán căn hộ Quận 1',
          description: 'Căn hộ 80m2 view sông Sài Gòn',
        }),
      },
    };

    const mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), verbose: vi.fn() };

    handler = new ListingCreatedModerationHandler(
      mockAiClient,
      mockCommandBus,
      mockPrisma as never,
      mockLogger as any,
    );
  });

  it('skips moderation when property not found', async () => {
    mockPrisma.property.findUnique.mockResolvedValue(null);

    await handler.handle(event);

    expect(mockAiClient.moderate).not.toHaveBeenCalled();
  });

  it('does not dispatch command when text is clean', async () => {
    (mockAiClient.moderate as ReturnType<typeof vi.fn>).mockResolvedValue({
      is_flagged: false,
      score: 0,
      flags: [],
      cleaned_text: 'Bán căn hộ Quận 1\nCăn hộ 80m2 view sông Sài Gòn',
    });

    await handler.handle(event);

    expect(mockCommandBus.execute).not.toHaveBeenCalled();
  });

  it('dispatches approve command for low-score flagged listing', async () => {
    (mockAiClient.moderate as ReturnType<typeof vi.fn>).mockResolvedValue({
      is_flagged: true,
      score: 0.5,
      flags: [
        { category: 'contact_info', severity: 'medium', matched_text: '0901234567', reason: 'Contact info detected' },
      ],
      cleaned_text: 'Bán căn hộ [REDACTED]',
    });

    await handler.handle(event);

    expect(mockCommandBus.execute).toHaveBeenCalledWith(
      expect.objectContaining({
        listingId: 'listing-1',
        action: 'approve',
        moderationScore: 0.5,
      }),
    );
  });

  it('dispatches reject command for high-score flagged listing', async () => {
    (mockAiClient.moderate as ReturnType<typeof vi.fn>).mockResolvedValue({
      is_flagged: true,
      score: 0.9,
      flags: [
        { category: 'profanity', severity: 'high', matched_text: 'lừa đảo', reason: 'Harmful language' },
        { category: 'prohibited_content', severity: 'high', matched_text: 'đất tranh chấp', reason: 'Prohibited property' },
      ],
      cleaned_text: '[REDACTED] [REDACTED]',
    });

    await handler.handle(event);

    expect(mockCommandBus.execute).toHaveBeenCalledWith(
      expect.objectContaining({
        listingId: 'listing-1',
        action: 'reject',
        moderationScore: 0.9,
      }),
    );
    const cmd = (mockCommandBus.execute as ReturnType<typeof vi.fn>).mock.calls[0][0] as ModerateListingCommand;
    expect(cmd.notes).toContain('[AI Auto-Moderation]');
    expect(cmd.moderatorId).toBe('system:ai-moderation');
  });

  it('silently handles AI service errors', async () => {
    (mockAiClient.moderate as ReturnType<typeof vi.fn>).mockRejectedValue(
      new Error('ECONNREFUSED'),
    );

    await expect(handler.handle(event)).resolves.not.toThrow();
    expect(mockCommandBus.execute).not.toHaveBeenCalled();
  });
});
