import {
  advanceDirectMessageStatus,
  createDirectConversation,
  createGroupConversation,
  directMessageEventIncludesUser,
  directMessageSyncIncludesUser,
  getDirectConversationId,
  isGroupDirectConversation,
  updateMessageStatusInConversation,
  upsertDirectMessage
} from '../../domain/logic/direct-message.logic';
import type { DirectMessage, DirectMessageParticipant } from '../../domain/models/direct-message.model';

const alice: DirectMessageParticipant = {
  userId: 'alice',
  username: 'alice',
  displayName: 'Alice'
};
const bob: DirectMessageParticipant = {
  userId: 'bob',
  username: 'bob',
  displayName: 'Bob'
};
const charlie: DirectMessageParticipant = {
  userId: 'charlie',
  username: 'charlie',
  displayName: 'Charlie'
};

describe('DirectMessageService domain flow', () => {
  it('should create conversation', () => {
    const conversation = createDirectConversation(alice, bob, 10);

    expect(conversation.id).toBe(getDirectConversationId('alice', 'bob'));
    expect(conversation.participants).toEqual(['alice', 'bob']);
    expect(conversation.unreadCount).toBe(0);
  });

  it('should send message', () => {
    const conversation = createDirectConversation(alice, bob, 10);
    const queuedMessage = createMessage('message-1', 'QUEUED');
    const withQueuedMessage = upsertDirectMessage(conversation, queuedMessage, false);
    const withSentMessage = updateMessageStatusInConversation(withQueuedMessage, queuedMessage.id, 'SENT');

    expect(withSentMessage.messages[0].status).toBe('SENT');
  });

  it('should queue message when offline', () => {
    const conversation = createDirectConversation(alice, bob, 10);
    const queuedMessage = createMessage('message-1', 'QUEUED');
    const updatedConversation = upsertDirectMessage(conversation, queuedMessage, false);

    expect(updatedConversation.messages[0].status).toBe('QUEUED');
  });

  it('should create empty group conversation without direct-message history', () => {
    const directConversation = upsertDirectMessage(createDirectConversation(alice, bob, 10), createMessage('message-1', 'SENT'), false);
    const groupConversation = createGroupConversation('dm-group-test', [
      alice,
      bob,
      charlie
    ], 30, 'Alice, Bob, Charlie');

    expect(isGroupDirectConversation(groupConversation)).toBe(true);
    expect(groupConversation.id).toBe('dm-group-test');
    expect(groupConversation.title).toBe('Alice, Bob, Charlie');
    expect(groupConversation.participants).toEqual([
      'alice',
      'bob',
      'charlie'
    ]);

    expect(groupConversation.messages).toEqual([]);
    expect(directConversation.messages).toHaveLength(1);
  });

  it('should preserve group message recipient metadata', () => {
    const conversation = createGroupConversation('dm-group-test', [
      alice,
      bob,
      charlie
    ], 10);
    const recipientIds = ['bob', 'charlie'];
    const message = createMessage('message-1', 'QUEUED', conversation.id, recipientIds);
    const updatedConversation = upsertDirectMessage(conversation, message, false);

    expect(updatedConversation.messages[0].recipientId).toBe('bob');
    expect(updatedConversation.messages[0].recipientIds).toEqual(recipientIds);
  });

  it('should update status correctly', () => {
    expect(advanceDirectMessageStatus('QUEUED', 'SENT')).toBe('SENT');
    expect(advanceDirectMessageStatus('SENT', 'DELIVERED')).toBe('DELIVERED');
    expect(advanceDirectMessageStatus('DELIVERED', 'SENT')).toBe('DELIVERED');
    expect(advanceDirectMessageStatus('DELIVERED', 'ACKNOWLEDGED')).toBe('ACKNOWLEDGED');
  });

  it('recognises only declared direct-message recipients and participants', () => {
    const payload = {
      message: createMessage('message-1', 'SENT', 'dm-group-test', ['bob']),
      participants: [alice, bob],
      sender: alice
    };

    expect(directMessageEventIncludesUser(payload, 'bob')).toBe(true);
    expect(directMessageEventIncludesUser(payload, 'charlie')).toBe(false);
  });

  it('recognises only declared sync participants', () => {
    const payload = {
      conversationId: 'dm-group-test',
      messages: [],
      participants: [alice, bob],
      sender: alice,
      syncedAt: 30
    };

    expect(directMessageSyncIncludesUser(payload, 'alice')).toBe(true);
    expect(directMessageSyncIncludesUser(payload, 'charlie')).toBe(false);
  });
});

function createMessage(
  id: string,
  status: DirectMessage['status'],
  conversationId = getDirectConversationId('alice', 'bob'),
  recipientIds = ['bob']
): DirectMessage {
  return {
    id,
    conversationId,
    senderId: 'alice',
    recipientId: recipientIds[0],
    recipientIds,
    content: 'Hello',
    timestamp: 20,
    status
  };
}
