import type { MessageResponse } from 'stream-chat';

import { selectMessagesForChannels } from './queries/selectMessagesForChannels';

import { selectReactionsForMessages } from './queries/selectReactionsForMessages';

import { mapStorableToMessage } from '../mappers/mapStorableToMessage';
import { createSelectQuery } from '../sqlite-utils/createSelectQuery';
import { SqliteClient } from '../SqliteClient';
import type { TableRow, TableRowJoinedUser } from '../types';

export const getChannelMessages = async ({
  channelIds,
  currentUserId,
}: {
  channelIds: string[];
  currentUserId: string;
}) => {
  SqliteClient.logger?.('info', 'getChannelMessages', {
    channelIds,
    currentUserId,
  });
  const messageRows = await selectMessagesForChannels(channelIds);
  const messageIds = messageRows.map(({ id }) => id);

  // Populate the message reactions.
  const reactionRows = await selectReactionsForMessages(messageIds, null);
  const messageIdVsReactions: Record<string, TableRowJoinedUser<'reactions'>[]> = {};
  reactionRows.forEach((reaction) => {
    if (!messageIdVsReactions[reaction.messageId]) {
      messageIdVsReactions[reaction.messageId] = [];
    }
    messageIdVsReactions[reaction.messageId].push(reaction);
  });

  // Populate the polls.
  const messageIdsVsPolls: Record<string, TableRow<'poll'>> = {};
  const pollsById: Record<string, TableRow<'poll'>> = {};
  const messagesWithPolls = messageRows.filter((message) => !!message.poll_id);
  const polls = (await SqliteClient.executeSql.apply(
    null,
    createSelectQuery('poll', ['*'], {
      id: messagesWithPolls.map((message) => message.poll_id),
    }),
  )) as unknown as TableRow<'poll'>[];
  polls.forEach((poll) => {
    pollsById[poll.id] = poll;
  });
  messagesWithPolls.forEach((message) => {
    messageIdsVsPolls[message.poll_id] = pollsById[message.poll_id];
  });

  const messageIdsVsReminders: Record<string, TableRow<'reminders'>> = {};
  const reminders = (await SqliteClient.executeSql.apply(
    null,
    createSelectQuery('reminders', ['*'], {
      messageId: messageIds,
    }),
  )) as unknown as TableRow<'reminders'>[];
  reminders.forEach((reminder) => {
    messageIdsVsReminders[reminder.messageId] = reminder;
  });

  const messagesWithSharedLocations = messageRows.filter((message) => !!message.shared_location);
  const messageIdsVsLocations: Record<string, TableRow<'locations'>> = {};
  const sharedLocationRows = (await SqliteClient.executeSql.apply(
    null,
    createSelectQuery('locations', ['*'], {
      messageId: messagesWithSharedLocations.map((message) => message.id),
    }),
  )) as unknown as TableRow<'locations'>[];

  sharedLocationRows.forEach((location) => {
    messageIdsVsLocations[location.messageId] = location;
  });

  // Populate the messages.
  const cidVsMessages: Record<string, MessageResponse[]> = {};
  messageRows.forEach((m) => {
    if (!cidVsMessages[m.cid]) {
      cidVsMessages[m.cid] = [];
    }

    cidVsMessages[m.cid].push(
      mapStorableToMessage({
        currentUserId,
        messageRow: m,
        pollRow: messageIdsVsPolls[m.poll_id],
        reactionRows: messageIdVsReactions[m.id],
        reminderRow: messageIdsVsReminders[m.id],
      }),
    );
  });

  return cidVsMessages;
};
