import { CreateConversationCommand } from '../../application/commands/create-conversation/create-conversation.command';
import { MarkConversationReadCommand } from '../../application/commands/mark-read/mark-read.command';
import { SendMessageCommand } from '../../application/commands/send-message/send-message.command';
import { GetConversationsQuery } from '../../application/queries/get-conversations/get-conversations.query';
import { GetMessagesQuery } from '../../application/queries/get-messages/get-messages.query';
import { MessagingController } from '../controllers/messaging.controller';

describe('MessagingController', () => {
  let controller: MessagingController;
  let mockCommandBus: { execute: ReturnType<typeof vi.fn> };
  let mockQueryBus: { execute: ReturnType<typeof vi.fn> };
  let mockMessageRepo: { softDelete: ReturnType<typeof vi.fn> };

  const user = { sub: 'user-1', role: 'BUYER' };

  beforeEach(() => {
    mockCommandBus = { execute: vi.fn() };
    mockQueryBus = { execute: vi.fn() };
    mockMessageRepo = { softDelete: vi.fn() };

    controller = new MessagingController(
      mockCommandBus as any,
      mockQueryBus as any,
      mockMessageRepo as any,
    );
  });

  describe('createConversation', () => {
    it('dispatches CreateConversationCommand', async () => {
      const dto = { participantUserId: 'user-2', listingId: 'listing-1', initialMessage: 'Hi' };
      mockCommandBus.execute.mockResolvedValue({ id: 'conv-1' });

      const result = await controller.createConversation(user as any, dto as any);

      expect(mockCommandBus.execute).toHaveBeenCalledWith(
        expect.any(CreateConversationCommand),
      );
      expect(result.id).toBe('conv-1');
    });
  });

  describe('getConversations', () => {
    it('dispatches GetConversationsQuery', async () => {
      mockQueryBus.execute.mockResolvedValue({ conversations: [], total: 0 });

      await controller.getConversations(user as any, 20, 0);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        expect.any(GetConversationsQuery),
      );
    });
  });

  describe('getMessages', () => {
    it('dispatches GetMessagesQuery', async () => {
      mockQueryBus.execute.mockResolvedValue([]);

      await controller.getMessages(user as any, 'conv-1', 50);

      expect(mockQueryBus.execute).toHaveBeenCalledWith(
        expect.any(GetMessagesQuery),
      );
    });
  });

  describe('sendMessage', () => {
    it('dispatches SendMessageCommand', async () => {
      const dto = { content: 'Hello!' };
      mockCommandBus.execute.mockResolvedValue({ id: 'msg-1' });

      const result = await controller.sendMessage(user as any, 'conv-1', dto as any);

      expect(mockCommandBus.execute).toHaveBeenCalledWith(
        expect.any(SendMessageCommand),
      );
      expect(result.id).toBe('msg-1');
    });
  });

  describe('markAsRead', () => {
    it('dispatches MarkConversationReadCommand', async () => {
      await controller.markAsRead(user as any, 'conv-1');

      expect(mockCommandBus.execute).toHaveBeenCalledWith(
        expect.any(MarkConversationReadCommand),
      );
    });
  });

  describe('deleteMessage', () => {
    it('calls messageRepo.softDelete with user id', async () => {
      await controller.deleteMessage(user as any, 'msg-1');

      expect(mockMessageRepo.softDelete).toHaveBeenCalledWith('msg-1', 'user-1');
    });
  });
});
