import type { EventBus } from '@nestjs/cqrs';
import { InquiryEntity } from '../../domain/entities/inquiry.entity';
import type { IInquiryRepository } from '../../domain/repositories/inquiry.repository';
import { MarkInquiryReadCommand } from '../commands/mark-inquiry-read/mark-inquiry-read.command';
import { MarkInquiryReadHandler } from '../commands/mark-inquiry-read/mark-inquiry-read.handler';

describe('MarkInquiryReadHandler', () => {
  let handler: MarkInquiryReadHandler;
  let mockInquiryRepo: { [K in keyof IInquiryRepository]: ReturnType<typeof vi.fn> };
  let mockEventBus: { publish: ReturnType<typeof vi.fn> };
  let mockPrisma: {
    listing: { findUnique: ReturnType<typeof vi.fn> };
    agent: { findUnique: ReturnType<typeof vi.fn> };
  };

  beforeEach(() => {
    mockInquiryRepo = {
      findById: vi.fn(),
      save: vi.fn(),
      markAsRead: vi.fn(),
      findByListing: vi.fn(),
      findByAgent: vi.fn(),
      countUnreadByAgent: vi.fn(),
    };

    mockEventBus = { publish: vi.fn() };

    mockPrisma = {
      listing: { findUnique: vi.fn() },
      agent: { findUnique: vi.fn() },
    };

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

    handler = new MarkInquiryReadHandler(
      mockInquiryRepo as any,
      mockEventBus as unknown as EventBus,
      mockPrisma as any,
      mockLogger as any,
    );
  });

  it('marks an inquiry as read successfully', async () => {
    const inquiry = new InquiryEntity('inq-1', {
      listingId: 'listing-1',
      userId: 'user-1',
      message: 'Tôi muốn xem nhà',
      phone: null,
      isRead: false,
    });
    mockInquiryRepo.findById.mockResolvedValue(inquiry);
    mockPrisma.listing.findUnique.mockResolvedValue({ agentId: 'agent-1' });
    mockPrisma.agent.findUnique.mockResolvedValue({ id: 'agent-1' });
    mockInquiryRepo.markAsRead.mockResolvedValue(undefined);

    const command = new MarkInquiryReadCommand('inq-1', 'agent-user-1');

    await handler.execute(command);

    expect(mockInquiryRepo.markAsRead).toHaveBeenCalledWith('inq-1');
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

  it('throws NotFoundException when inquiry not found', async () => {
    mockInquiryRepo.findById.mockResolvedValue(null);

    const command = new MarkInquiryReadCommand('inq-not-exist', 'agent-user-1');

    await expect(handler.execute(command)).rejects.toThrow(
      "Inquiry with id 'inq-not-exist' not found",
    );
  });

  it('throws NotFoundException when listing not found', async () => {
    const inquiry = new InquiryEntity('inq-1', {
      listingId: 'listing-1',
      userId: 'user-1',
      message: 'Test',
      phone: null,
      isRead: false,
    });
    mockInquiryRepo.findById.mockResolvedValue(inquiry);
    mockPrisma.listing.findUnique.mockResolvedValue(null);

    const command = new MarkInquiryReadCommand('inq-1', 'agent-user-1');

    await expect(handler.execute(command)).rejects.toThrow(
      "Listing with id 'listing-1' not found",
    );
  });

  it('throws ForbiddenException when user is not the listing agent', async () => {
    const inquiry = new InquiryEntity('inq-1', {
      listingId: 'listing-1',
      userId: 'user-1',
      message: 'Test',
      phone: null,
      isRead: false,
    });
    mockInquiryRepo.findById.mockResolvedValue(inquiry);
    mockPrisma.listing.findUnique.mockResolvedValue({ agentId: 'agent-1' });
    mockPrisma.agent.findUnique.mockResolvedValue({ id: 'agent-other' });

    const command = new MarkInquiryReadCommand('inq-1', 'agent-user-other');

    await expect(handler.execute(command)).rejects.toThrow(
      'Bạn không có quyền đánh dấu yêu cầu tư vấn này',
    );
  });

  it('throws ForbiddenException when agent not found for user', async () => {
    const inquiry = new InquiryEntity('inq-1', {
      listingId: 'listing-1',
      userId: 'user-1',
      message: 'Test',
      phone: null,
      isRead: false,
    });
    mockInquiryRepo.findById.mockResolvedValue(inquiry);
    mockPrisma.listing.findUnique.mockResolvedValue({ agentId: 'agent-1' });
    mockPrisma.agent.findUnique.mockResolvedValue(null);

    const command = new MarkInquiryReadCommand('inq-1', 'not-an-agent');

    await expect(handler.execute(command)).rejects.toThrow(
      'Bạn không có quyền đánh dấu yêu cầu tư vấn này',
    );
  });
});
