import type { Message, Room } from '../../../../shared-kernel';
import type {
  NotificationDeliveryContext,
  NotificationDisplayPayload,
  NotificationsSettings,
  RoomUnreadCounts
} from '../models/notification.model';

export const DEFAULT_TEXT_CHANNEL_ID = 'general';

const MESSAGE_PREVIEW_LIMIT = 140;

export type AppTranslateFn = (key: string, params?: Record<string, string | number>) => string;

export function resolveMessageChannelId(message: Pick<Message, 'channelId'>): string {
  return message.channelId || DEFAULT_TEXT_CHANNEL_ID;
}

export function getRoomTextChannelIds(room: Room): string[] {
  const textChannelIds = (room.channels ?? [])
    .filter((channel) => channel.type === 'text')
    .map((channel) => channel.id);

  return textChannelIds.length > 0 ? textChannelIds : [DEFAULT_TEXT_CHANNEL_ID];
}

export function getRoomById(rooms: Room[], roomId: string): Room | null {
  return rooms.find((room) => room.id === roomId) ?? null;
}

export function getChannelLabel(room: Room | null, channelId: string): string {
  const channelName = room?.channels?.find((channel) => channel.id === channelId)?.name;

  return channelName || DEFAULT_TEXT_CHANNEL_ID;
}

export function getChannelLastReadAt(
  settings: NotificationsSettings,
  roomId: string,
  channelId: string
): number {
  return settings.lastReadByChannel[roomId]?.[channelId]
    ?? settings.roomBaselines[roomId]
    ?? 0;
}

export function getRoomTrackingBaseline(settings: NotificationsSettings, room: Room): number {
  const trackedChannels = getRoomTextChannelIds(room).map((channelId) =>
    getChannelLastReadAt(settings, room.id, channelId)
  );

  return Math.min(...trackedChannels, settings.roomBaselines[room.id] ?? Date.now());
}

export function isRoomMuted(settings: NotificationsSettings, roomId: string): boolean {
  return settings.mutedRooms[roomId] === true;
}

export function isChannelMuted(
  settings: NotificationsSettings,
  roomId: string,
  channelId: string
): boolean {
  return settings.mutedChannels[roomId]?.[channelId] === true;
}

export function isMessageVisibleInActiveView(
  message: Pick<Message, 'channelId' | 'roomId'>,
  context: NotificationDeliveryContext
): boolean {
  return context.currentRoomId === message.roomId
    && context.activeChannelId === resolveMessageChannelId(message)
    && context.isWindowFocused
    && context.isDocumentVisible;
}

export function shouldDeliverNotification(
  settings: NotificationsSettings,
  message: Pick<Message, 'channelId' | 'roomId'>,
  context: NotificationDeliveryContext
): boolean {
  const channelId = resolveMessageChannelId(message);

  if (!settings.enabled) {
    return false;
  }

  if (context.currentUser?.status === 'busy') {
    return false;
  }

  if (isRoomMuted(settings, message.roomId) || isChannelMuted(settings, message.roomId, channelId)) {
    return false;
  }

  return !isMessageVisibleInActiveView(message, context);
}

export function buildNotificationDisplayPayload(
  message: Pick<Message, 'channelId' | 'content' | 'senderName'>,
  room: Room | null,
  settings: NotificationsSettings,
  requestAttention: boolean,
  translate: AppTranslateFn
): NotificationDisplayPayload {
  const channelId = resolveMessageChannelId(message);
  const roomName = room?.name || translate('notifications.display.defaultServerName');
  const channelLabel = getChannelLabel(room, channelId);

  return {
    title: `${roomName} · #${channelLabel}`,
    body: settings.showPreview
      ? formatMessagePreview(message.senderName, message.content, translate)
      : translate('notifications.display.newMessageHidden', { sender: message.senderName }),
    requestAttention
  };
}

export function calculateUnreadForRoom(
  room: Room,
  messages: Message[],
  settings: NotificationsSettings,
  currentUserIds: Set<string>
): RoomUnreadCounts {
  const channelCounts = Object.fromEntries(
    getRoomTextChannelIds(room).map((channelId) => [channelId, 0])
  ) as Record<string, number>;

  for (const message of messages) {
    if (message.isDeleted || currentUserIds.has(message.senderId)) {
      continue;
    }

    const channelId = resolveMessageChannelId(message);

    if (!(channelId in channelCounts)) {
      continue;
    }

    if (message.timestamp <= getChannelLastReadAt(settings, room.id, channelId)) {
      continue;
    }

    channelCounts[channelId] += 1;
  }

  return {
    channelCounts,
    roomCount: Object.values(channelCounts).reduce((total, count) => total + count, 0)
  };
}

function formatMessagePreview(senderName: string, content: string, translate: AppTranslateFn): string {
  const normalisedContent = content.replace(/\s+/g, ' ').trim();

  if (!normalisedContent) {
    return translate('notifications.display.newMessageEmpty', { sender: senderName });
  }

  const preview = normalisedContent.length > MESSAGE_PREVIEW_LIMIT
    ? `${normalisedContent.slice(0, MESSAGE_PREVIEW_LIMIT - 1)}...`
    : normalisedContent;

  return translate('notifications.display.preview', { sender: senderName, content: preview });
}
