import { MarkConversationReadCommand } from '../commands/mark-read/mark-read.command';
import { MarkConversationReadHandler } from '../commands/mark-read/mark-read.handler';

describe('MarkConversationReadHandler', () => {
  let handler: MarkConversationReadHandler;
  let mockConversationRepo: {
    findById: ReturnType<typeof vi.fn>;
    resetUnreadCount: ReturnType<typeof vi.fn>;
  };
  let mockLogger: { log: ReturnType<typeof vi.fn>; warn: ReturnType<typeof vi.fn>; error: ReturnType<typeof vi.fn> };

  let mockEventBus: { publish: ReturnType<typeof vi.fn> };

  const conversation = {
    id: 'conv-1',
    status: 'ACTIVE' as const,
    participants: [
      { id: 'p-1', conversationId: 'conv-1', userId: 'user-1', unreadCount: 3, lastReadAt: null, joinedAt: new Date() },
      { id: 'p-2', conversationId: 'conv-1', userId: 'user-2', unreadCount: 0, lastReadAt: null, joinedAt: new Date() },
    ],
  };

  beforeEach(() => {
    mockConversationRepo = {
      findById: vi.fn().mockResolvedValue(conversation),
      resetUnreadCount: vi.fn().mockResolvedValue(undefined),
    };
    mockEventBus = { publish: vi.fn() };
    mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn() };

    handler = new MarkConversationReadHandler(
      mockConversationRepo as any,
      mockEventBus as any,
      mockLogger as any,
    );
  });

  it('marks conversation as read for the user', async () => {
    const command = new MarkConversationReadCommand('conv-1', 'user-1');

    await handler.execute(command);

    expect(mockConversationRepo.resetUnreadCount).toHaveBeenCalledWith('conv-1', 'user-1');
    expect(mockEventBus.publish).toHaveBeenCalledWith(
      expect.objectContaining({
        eventName: 'conversation.read',
        conversationId: 'conv-1',
        userId: 'user-1',
      }),
    );
  });

  it('throws NotFoundException when conversation does not exist', async () => {
    mockConversationRepo.findById.mockResolvedValue(null);

    const command = new MarkConversationReadCommand('conv-999', 'user-1');

    await expect(handler.execute(command)).rejects.toThrow();
  });

  it('throws ForbiddenException when user is not a participant', async () => {
    const command = new MarkConversationReadCommand('conv-1', 'user-3');

    await expect(handler.execute(command)).rejects.toThrow('Bạn không phải là thành viên của hội thoại này');
  });
});
