import type {
  DirectMessage,
  DirectMessageConversation,
  DirectMessageEventPayload,
  DirectMessageParticipant,
  DirectMessageSyncEventPayload,
  DirectMessageStatus
} from '../models/direct-message.model';

const STATUS_ORDER: Record<DirectMessageStatus, number> = {
  QUEUED: 0,
  SENT: 1,
  DELIVERED: 2,
  ACKNOWLEDGED: 3
};

export function getDirectConversationId(firstUserId: string, secondUserId: string): string {
  return `dm-${[firstUserId, secondUserId]
    .map((userId) => encodeURIComponent(userId.trim()))
    .sort()
    .join('--')}`;
}

export function advanceDirectMessageStatus(
  currentStatus: DirectMessageStatus,
  incomingStatus: DirectMessageStatus
): DirectMessageStatus {
  return STATUS_ORDER[incomingStatus] > STATUS_ORDER[currentStatus]
    ? incomingStatus
    : currentStatus;
}

export function createDirectConversation(
  currentUser: DirectMessageParticipant,
  peer: DirectMessageParticipant,
  now: number
): DirectMessageConversation {
  const participants = [currentUser.userId, peer.userId].sort();

  return {
    id: getDirectConversationId(currentUser.userId, peer.userId),
    kind: 'direct',
    participants,
    participantProfiles: {
      [currentUser.userId]: currentUser,
      [peer.userId]: peer
    },
    messages: [],
    lastMessageAt: now,
    unreadCount: 0
  };
}

export function createGroupConversation(
  conversationId: string,
  participants: DirectMessageParticipant[],
  now: number,
  title?: string
): DirectMessageConversation {
  const uniqueParticipants = uniqueDirectMessageParticipants(participants);
  const participantIds = uniqueParticipants.map((participant) => participant.userId).sort();

  return {
    id: conversationId,
    kind: 'group',
    title: title || buildGroupConversationTitle(uniqueParticipants),
    participants: participantIds,
    participantProfiles: Object.fromEntries(uniqueParticipants.map((participant) => [participant.userId, participant])),
    messages: [],
    lastMessageAt: now,
    unreadCount: 0
  };
}

export function isGroupDirectConversation(conversation: DirectMessageConversation): boolean {
  return conversation.kind === 'group' || conversation.participants.length > 2;
}

export function directMessageConversationIncludesUser(
  conversation: Pick<DirectMessageConversation, 'participantProfiles' | 'participants'>,
  userId: string
): boolean {
  return conversation.participants.includes(userId) || !!conversation.participantProfiles[userId];
}

export function directMessageEventIncludesUser(
  payload: DirectMessageEventPayload,
  userId: string
): boolean {
  return collectDirectMessageEventParticipantIds(payload).has(userId);
}

export function directMessageSyncIncludesUser(
  payload: DirectMessageSyncEventPayload,
  userId: string
): boolean {
  return payload.participants.some((participant) => participant.userId === userId);
}

export function upsertDirectMessage(
  conversation: DirectMessageConversation,
  message: DirectMessage,
  incrementUnread: boolean
): DirectMessageConversation {
  const existingIndex = conversation.messages.findIndex((entry) => entry.id === message.id);
  const messages = [...conversation.messages];

  if (existingIndex >= 0) {
    const existing = messages[existingIndex];

    messages[existingIndex] = {
      ...existing,
      ...message,
      status: advanceDirectMessageStatus(existing.status, message.status)
    };
  } else {
    messages.push(message);
  }

  messages.sort((firstMessage, secondMessage) => firstMessage.timestamp - secondMessage.timestamp);

  return {
    ...conversation,
    messages,
    lastMessageAt: Math.max(conversation.lastMessageAt, message.timestamp),
    unreadCount: incrementUnread ? conversation.unreadCount + 1 : conversation.unreadCount
  };
}

export function updateMessageStatusInConversation(
  conversation: DirectMessageConversation,
  messageId: string,
  status: DirectMessageStatus
): DirectMessageConversation {
  const messages = conversation.messages.map((message) => message.id === messageId
    ? { ...message, status: advanceDirectMessageStatus(message.status, status) }
    : message);

  return { ...conversation, messages };
}

function uniqueDirectMessageParticipants(participants: DirectMessageParticipant[]): DirectMessageParticipant[] {
  const seen = new Set<string>();

  return participants.filter((participant) => {
    if (!participant.userId || seen.has(participant.userId)) {
      return false;
    }

    seen.add(participant.userId);
    return true;
  });
}

function collectDirectMessageEventParticipantIds(payload: DirectMessageEventPayload): Set<string> {
  const participantIds = new Set<string>();

  if (payload.message.senderId) {
    participantIds.add(payload.message.senderId);
  }

  if (payload.message.recipientId) {
    participantIds.add(payload.message.recipientId);
  }

  for (const recipientId of payload.message.recipientIds ?? []) {
    participantIds.add(recipientId);
  }

  for (const participant of payload.participants ?? []) {
    if (participant.userId) {
      participantIds.add(participant.userId);
    }
  }

  return participantIds;
}

function buildGroupConversationTitle(participants: DirectMessageParticipant[]): string {
  const names = participants.map((participant) => participant.displayName || participant.username || participant.userId);

  if (names.length <= 3) {
    return names.join(', ');
  }

  return `${names.slice(0, 3).join(', ')} +${names.length - 3}`;
}
