import {TaggedUser} from '@interfaces/taggedUsers';
import {
  ChatUserStatus,
  HighlightedSpan,
  HighlightedSpanType,
} from '@modules/chats/interfaces';
import {getUserInfo} from '@modules/chats/services';
import {ALL_MENTION, DEFAULT_MENTION_NAME} from '@modules/chats/constants';
import {ShortedUser, usersMapper} from '@modules/chats/instances/UsersMapper';
import {NBSP} from '@shared/constants';
import {sentryLogs} from '@shared/utils';

export const REGEX_MENTION_USER_ID_CAPTURE_ALL = new RegExp(
  `<@U(\\d+)>|${ALL_MENTION.command}`,
  'g',
);

function replaceRange({
  str,
  start,
  end,
  replacement,
}: {
  str: string;
  start: number;
  end: number;
  replacement: string;
}) {
  return str.slice(0, start) + replacement + str.slice(end);
}

export const getTextWithHighlights = async ({
  message,
  highlightedSpans,
}: {
  message: string;
  highlightedSpans: HighlightedSpan[];
}) => {
  let updatedMessage = message;

  try {
    // Iterate backwards to avoid index shifting issues when replacing text
    for (let i = highlightedSpans.length - 1; i >= 0; i--) {
      const span = highlightedSpans[i];

      if (span.type === HighlightedSpanType.Mention) {
        const originalText = updatedMessage.slice(span.start, span.end + 1);
        const hasTrailingSpace = originalText.endsWith(' ');
        let finalMemberInfo = usersMapper.get(span.user_id);

        // If the member is not in the local store, fetch it from the DB or server
        if (!finalMemberInfo) {
          try {
            const [user] = await getUserInfo({user: span.user_id});
            finalMemberInfo = {
              id: user.id,
              first_name: user.first_name,
              last_name: user.last_name,
              profile_picture: user.picture_asset_url,
              status: user.status as ChatUserStatus,
              deleted_at: user.deleted_at || null,
            };

            usersMapper.save([finalMemberInfo]);
          } catch (error) {
            const e = error as Error;
            sentryLogs.error({
              message: 'Getting user info for mention failed',
              module: 'CHATS',
              component: 'getTextWithHighlights',
              data: {
                span,
                error: e.message ?? e,
              },
            });
          }
        }

        const name = getFullNameForMention({user: finalMemberInfo, span});

        updatedMessage = replaceRange({
          start: span.start,
          end: hasTrailingSpace ? span.end + 1 : span.end,
          str: updatedMessage,
          replacement: `@${name}` + (hasTrailingSpace ? ' ' : ''),
        });
      } else if (span.type === HighlightedSpanType.AllMention) {
        const originalText = updatedMessage.slice(span.start, span.end);
        const hasTrailingSpace = originalText.endsWith(' ');
        updatedMessage = replaceRange({
          start: span.start,
          end: hasTrailingSpace ? span.end + 1 : span.end,
          str: updatedMessage,
          replacement: `@${ALL_MENTION.label}`,
        });
      }
    }

    return updatedMessage;
  } catch (error) {
    return updatedMessage;
  }
};

export function prepareMessageWithMentions(
  message: string,
  taggedUsers: TaggedUser[],
) {
  if (!taggedUsers.length) {
    return message;
  }

  let updatedMessage = message;
  let offset = 0;

  taggedUsers.forEach(
    ({
      start,
      length,
      type: {
        user: {id},
      },
    }) => {
      const mention = id === ALL_MENTION.id ? ALL_MENTION.command : `<@U${id}>`;
      const originalStart = start + offset;
      const originalEnd = originalStart + length;

      updatedMessage = replaceRange({
        str: updatedMessage,
        start: originalStart,
        end: originalEnd,
        replacement: mention,
      });

      offset += mention.length - length;
    },
  );

  return updatedMessage;
}

export const getHighlightedSpans = (messageText: string): HighlightedSpan[] => {
  const matches = messageText.matchAll(REGEX_MENTION_USER_ID_CAPTURE_ALL);

  return Array.from(matches, match => {
    return match[0] === ALL_MENTION.command
      ? {
          type: HighlightedSpanType.AllMention,
          start: match.index!,
          end: match.index! + match[0].length,
        }
      : {
          type: HighlightedSpanType.Mention,
          user_id: parseInt(match[1], 10),
          start: match.index!,
          end: match.index! + match[0].length,
        };
  });
};

export const getFullNameForMention = ({
  user,
  span,
}: {
  user?: Pick<ShortedUser, 'first_name' | 'last_name'>;
  span: HighlightedSpan;
}) => {
  if (span.type === HighlightedSpanType.AllMention) {
    return ALL_MENTION.label;
  } else if (span.type === HighlightedSpanType.Mention) {
    return user
      ? `${user.first_name}${NBSP}${user.last_name}`
      : span.full_name ?? DEFAULT_MENTION_NAME;
  }

  // Should never happen
  return '';
};
