import { CreateConversationCommand } from '../commands/create-conversation/create-conversation.command';
import { CreateConversationHandler } from '../commands/create-conversation/create-conversation.handler';

describe('CreateConversationHandler', () => {
  let handler: CreateConversationHandler;
  let mockConversationRepo: {
    create: ReturnType<typeof vi.fn>;
    findExistingBetweenUsers: ReturnType<typeof vi.fn>;
    updateLastMessage: ReturnType<typeof vi.fn>;
    incrementUnreadCount: ReturnType<typeof vi.fn>;
    findById: ReturnType<typeof vi.fn>;
  };
  let mockMessageRepo: {
    create: ReturnType<typeof vi.fn>;
  };
  let mockEventBus: { publish: ReturnType<typeof vi.fn> };
  let mockLogger: { log: ReturnType<typeof vi.fn>; warn: ReturnType<typeof vi.fn>; error: ReturnType<typeof vi.fn> };

  const conversationEntity = {
    id: 'conv-1',
    listingId: 'listing-1',
    subject: null,
    status: 'ACTIVE' as const,
    lastMessage: null,
    lastMessageAt: null,
    createdAt: new Date(),
    updatedAt: new Date(),
    participants: [
      { id: 'p-1', conversationId: 'conv-1', userId: 'user-1', unreadCount: 0, lastReadAt: null, joinedAt: new Date() },
      { id: 'p-2', conversationId: 'conv-1', userId: 'user-2', unreadCount: 0, lastReadAt: null, joinedAt: new Date() },
    ],
  };

  const messageEntity = {
    id: 'msg-1',
    conversationId: 'conv-1',
    senderId: 'user-1',
    type: 'TEXT' as const,
    content: 'Hello!',
    metadata: null,
    editedAt: null,
    deletedAt: null,
    createdAt: new Date(),
  };

  beforeEach(() => {
    mockConversationRepo = {
      create: vi.fn().mockResolvedValue(conversationEntity),
      findExistingBetweenUsers: vi.fn().mockResolvedValue(null),
      updateLastMessage: vi.fn().mockResolvedValue(undefined),
      incrementUnreadCount: vi.fn().mockResolvedValue(undefined),
      findById: vi.fn(),
    };
    mockMessageRepo = {
      create: vi.fn().mockResolvedValue(messageEntity),
    };
    mockEventBus = { publish: vi.fn() };
    mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn() };

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

  it('creates a new conversation with participants', async () => {
    const command = new CreateConversationCommand('user-1', 'user-2', 'listing-1');

    const result = await handler.execute(command);

    expect(mockConversationRepo.findExistingBetweenUsers).toHaveBeenCalledWith(
      ['user-1', 'user-2'],
      'listing-1',
    );
    expect(mockConversationRepo.create).toHaveBeenCalledWith({
      listingId: 'listing-1',
      subject: undefined,
      participantUserIds: ['user-1', 'user-2'],
    });
    expect(result.id).toBe('conv-1');
  });

  it('creates conversation with initial message', async () => {
    const command = new CreateConversationCommand('user-1', 'user-2', 'listing-1', undefined, 'Hello!');

    await handler.execute(command);

    expect(mockMessageRepo.create).toHaveBeenCalledWith({
      conversationId: 'conv-1',
      senderId: 'user-1',
      type: 'TEXT',
      content: 'Hello!',
    });
    expect(mockConversationRepo.updateLastMessage).toHaveBeenCalled();
    expect(mockConversationRepo.incrementUnreadCount).toHaveBeenCalledWith('conv-1', 'user-1');
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

  it('returns existing conversation when one exists between users', async () => {
    mockConversationRepo.findExistingBetweenUsers.mockResolvedValue(conversationEntity);

    const command = new CreateConversationCommand('user-1', 'user-2', 'listing-1');

    const result = await handler.execute(command);

    expect(result.id).toBe('conv-1');
    expect(mockConversationRepo.create).not.toHaveBeenCalled();
  });

  it('sends message in existing conversation when initial message provided', async () => {
    mockConversationRepo.findExistingBetweenUsers.mockResolvedValue(conversationEntity);

    const command = new CreateConversationCommand('user-1', 'user-2', 'listing-1', undefined, 'Hello again!');

    await handler.execute(command);

    expect(mockConversationRepo.create).not.toHaveBeenCalled();
    expect(mockMessageRepo.create).toHaveBeenCalledWith({
      conversationId: 'conv-1',
      senderId: 'user-1',
      type: 'TEXT',
      content: 'Hello again!',
    });
  });

  it('throws when creating conversation with yourself', async () => {
    const command = new CreateConversationCommand('user-1', 'user-1');

    await expect(handler.execute(command)).rejects.toThrow('Không thể tạo hội thoại với chính mình');
  });
});
