/* eslint-disable no-case-declarations */
import {PixelRatio, StyleSheet} from 'react-native';
import TextSize from 'react-native-text-size';
import {
  Asset,
  Block,
  ExtendedMessage,
  ExtendedMessageType,
  Message,
  GifAsset,
  MessageStatus,
} from '@modules/chats/interfaces';
import {getMessageReactionsCount} from '@modules/chats/store/zustand/reactions';
import {TypographyWeight} from '@shared/components/_core/Typography/interfaces';
import {TYPOGRAPHY_STYLES} from '@shared/components/_core/Typography/styles';
import {windowDimensions} from '@shared/constants';
import {SPACING} from '@shared/theme';
import {isAndroid} from '@shared/utils';

export const fontScale = PixelRatio.getFontScale();
import {isMessageReplied, isSystemMessage} from './blocks';
import {MAX_REACTIONS_FOR_MESSAGE, MAX_REACTIONS_PER_LINE} from '../constants';
import {computeElegantNumber} from './computation';

export const CHATS_SPACING = {
  textLineHeight: 22.5,
  pillTextHeight: 28,
  unsupportedMessageTextHeight: 24,
  messageFooterTextHeight: 20,
  systemMessageHeight: 20,
  usernameLineHeight: 20,
  avatarHeight: 32,
  forwardedLabelHeight: SPACING.x2,
  smallGap: SPACING['x0.5'],
  gap: SPACING.x1,
  bigGap: SPACING.x2,
  fileHeight: 72,
  audioHeight: 62,
  footerAttachmentHeight: 72 + SPACING.x2,
  bigImageSize: 160,
  mediumImageSize: 120,
  smallImageSize: 80,
  scrollPadding: SPACING.x2,
  itemHorizontalPadding: SPACING.x2,
  bubbleHorizontalPadding: SPACING.x1,
  bubbleVerticalPadding: SPACING.x1,
  inputContainerHeight: 72,
  attachmentDialogHeight: 120,
  deletedMessageHeight: 24,
  replyLineHeight: 16.8,
  replyHeight: 50,
  reactionsHeight: 32,
  giphyDialogHeight: 450,
  gifHorizontalWidth: 272,
  gifVerticalWidth: 160,
  gifSquareWidth: 160,
  inputMinHeight: 40,
  inputMaxHeight: 120,
  audioBubbleWidth: windowDimensions.width * 0.64,
  errorMessageHeight: 20,
  errorIconSize: 24,
};

//AspectRatio by Giphy is handled by AR = width / height
export const calculateGifWidthAndHeight = (aspectRatio: number) => {
  let width = 0;
  let height = 0;
  if (aspectRatio > 1) {
    width = CHATS_SPACING.gifHorizontalWidth;
  }
  if (aspectRatio < 1) {
    width = CHATS_SPACING.gifVerticalWidth;
  }
  if (aspectRatio === 1) {
    width = CHATS_SPACING.gifSquareWidth;
  }
  height = Math.round(width / aspectRatio);
  return {width, height};
};

export const getReactionsLines = (reactionsAmount?: number): number => {
  if (!reactionsAmount) {
    return 0;
  }

  // Add 1 for the + button if we have space for more reactions
  const totalReactions =
    reactionsAmount < MAX_REACTIONS_FOR_MESSAGE
      ? reactionsAmount + 1
      : reactionsAmount;

  // Calculate how many lines we need
  return Math.ceil(totalReactions / MAX_REACTIONS_PER_LINE);
};

export const getReactionsHeight = (msgReactionsCount: number): number => {
  if (!msgReactionsCount) {
    return 0;
  }

  return (
    CHATS_SPACING.reactionsHeight * getReactionsLines(msgReactionsCount) +
    CHATS_SPACING.gap
  );
};

// window width - scroll padding (left + right) - avatar height + gap - bubble horizontal padding - border width (left + right)
export const MESSAGE_TEXT_MAX_WIDTH =
  windowDimensions.width -
  CHATS_SPACING.scrollPadding * 2 -
  CHATS_SPACING.avatarHeight -
  CHATS_SPACING.gap -
  CHATS_SPACING.itemHorizontalPadding * 2 -
  2;

/**
 * Styles for the text inside the message bubble
 */

export const MESSAGE_TEXT_STYLES = StyleSheet.create({
  text: {
    ...TYPOGRAPHY_STYLES.s,
    fontSize: computeElegantNumber(16 * fontScale),
    includeFontPadding: false,
    letterSpacing: computeElegantNumber(0.2 * fontScale),
    lineHeight: (() => {
      const value = computeElegantNumber(22.5 * fontScale);
      return isAndroid ? PixelRatio.roundToNearestPixel(value) : value;
    })(),
    maxWidth: MESSAGE_TEXT_MAX_WIDTH,
    overflow: 'hidden',
    padding: 0,
    textAlignVertical: 'top',
  },
}).text;

export const getMessageMargins = ({
  extendedType,
  useExtraMarginTop,
}: ExtendedMessage) => {
  if (extendedType === ExtendedMessageType.DatePill) {
    return {marginTop: CHATS_SPACING.gap, marginBottom: 0};
  }
  const isUnreadPill = extendedType === ExtendedMessageType.UnreadPill;
  const marginTop = isUnreadPill
    ? CHATS_SPACING.bigGap
    : useExtraMarginTop
    ? CHATS_SPACING.gap
    : CHATS_SPACING.smallGap;
  const marginBottom = isUnreadPill ? CHATS_SPACING.gap : 0;
  return {marginTop, marginBottom};
};

/**
 * Get the exact height of the attachments from the message
 */
export const getAttachmentsHeight = (attachments: Asset[]): number => {
  if (!attachments.length) {
    return 0;
  }

  if (attachments[0].metadata.format === 'file') {
    return CHATS_SPACING.fileHeight;
  }

  if (attachments[0].metadata.format === 'audio') {
    return CHATS_SPACING.audioHeight;
  }

  if (attachments[0].metadata.format === 'gif') {
    return (attachments[0] as GifAsset).image_height;
  }

  const isBig = attachments.length === 1;
  const isMedium = attachments.length === 2;

  return isBig
    ? CHATS_SPACING.bigImageSize
    : isMedium
    ? CHATS_SPACING.mediumImageSize
    : CHATS_SPACING.smallImageSize;
};

/**
 * Get the exact height of the message (ignoring the font scale).
 * Returns -1 if there was an error calculating the height.
 * Returns 0 if the message is a multimedia message.
 */
export const calculateTextHeight = async ({
  text,
  blocks,
}: {
  text?: string;
  blocks?: Block[];
}) => {
  // If it's a system message, return the height of the system message
  if (isSystemMessage({blocks})) {
    return CHATS_SPACING.systemMessageHeight * (blocks || []).length;
  }

  const message = text?.trim();

  if (!message || message.length === 0) {
    return 0;
  }

  try {
    // Get the line count of the text
    const lineCount = await TextSize.getLineCount({
      text: [message],
      width: MESSAGE_TEXT_MAX_WIDTH,
      fontSize: MESSAGE_TEXT_STYLES.fontSize,
      letterSpacing: MESSAGE_TEXT_STYLES.letterSpacing,
      fontWeight: MESSAGE_TEXT_STYLES.fontWeight as TypographyWeight.regular,
      fontFamily: MESSAGE_TEXT_STYLES.fontFamily,
      fontStyle: MESSAGE_TEXT_STYLES.fontStyle,
      textBreakStrategy: 'simple',
      allowFontScaling: false,
      includeFontPadding: false,
      lineHeight: MESSAGE_TEXT_STYLES.lineHeight,
    });

    // Return the height of the text in base font size
    return lineCount[0] * MESSAGE_TEXT_STYLES.lineHeight;
  } catch (error) {
    // Do nothing
  }

  // If there was an error calculating the height, return -1
  return 0;
};

/**
 * Get the estimated height of the message (including the font scale if the device has this accessibility feature enabled)
 */
export const getEstimatedMessageSize = (
  item: ExtendedMessage,
  _index: number,
) => {
  if (!item) {
    return 0;
  }

  const {marginTop, marginBottom} = getMessageMargins(item);

  switch (item.extendedType) {
    case ExtendedMessageType.ListPadding:
      return CHATS_SPACING.scrollPadding;

    case ExtendedMessageType.SystemMessage:
      return item.data.height + marginTop + marginBottom;

    case ExtendedMessageType.DatePill:
    case ExtendedMessageType.UnreadPill:
      return CHATS_SPACING.pillTextHeight + marginTop + marginBottom;

    case ExtendedMessageType.UnsupportedMessage:
      let unsupportedMessageSize =
        CHATS_SPACING.unsupportedMessageTextHeight +
        CHATS_SPACING.bubbleVerticalPadding * 2 +
        marginTop +
        marginBottom;

      if (item.showSenderInfo) {
        unsupportedMessageSize +=
          CHATS_SPACING.usernameLineHeight + CHATS_SPACING.smallGap;
      }

      return unsupportedMessageSize;

    case ExtendedMessageType.DeletedMessage:
      let deletedMessageSize =
        CHATS_SPACING.deletedMessageHeight +
        CHATS_SPACING.bubbleVerticalPadding * 2 +
        CHATS_SPACING.messageFooterTextHeight +
        CHATS_SPACING.smallGap +
        marginTop +
        marginBottom;

      if (item.showSenderInfo) {
        deletedMessageSize +=
          CHATS_SPACING.usernameLineHeight + CHATS_SPACING.smallGap;
      }
      return deletedMessageSize;

    case ExtendedMessageType.Reactions:
      const reactionsCount = getMessageReactionsCount(item.data.clientMsgId);
      return getReactionsHeight(reactionsCount);

    case ExtendedMessageType.Default:
    default:
      if (item?.data?.height === undefined || item?.data?.height === -1) {
        // Should not happen, but just in case
        return 0;
      }

      const isReplied = isMessageReplied(item.data);
      const isForwarded = !!item.data.forwarded;
      const isAudio = item.data.attachments?.at(0)?.metadata.format === 'audio';

      // Vertical padding +  margin bottom
      let size =
        CHATS_SPACING.bubbleVerticalPadding * 2 + marginTop + marginBottom;

      if (isForwarded) {
        size += CHATS_SPACING.forwardedLabelHeight;
      } else if (isReplied) {
        size += CHATS_SPACING.replyHeight + CHATS_SPACING.smallGap;
      }

      // Add attachments height if it has any
      if (isAudio || item.data.pendingAssets?.length) {
        size += CHATS_SPACING.audioHeight;
      } else {
        // Add footer height only for non-audio messages
        size += CHATS_SPACING.messageFooterTextHeight + CHATS_SPACING.smallGap;

        if (item.data.attachments?.length) {
          size +=
            getAttachmentsHeight(item.data.attachments) +
            CHATS_SPACING.smallGap;
        }
      }

      if (item.showSenderInfo) {
        size += CHATS_SPACING.usernameLineHeight + CHATS_SPACING.smallGap;
      }

      if (item.data.status === MessageStatus.Error) {
        size += CHATS_SPACING.errorMessageHeight + CHATS_SPACING.gap;
      }

      // If the height is 0, it means it's a multimedia message without text
      if (item.data.height === 0) {
        return size;
      }

      // Base height with font scale
      size += item.data.height;

      return size;
  }
};

export const meassureMultipleMessages = async (items: Message[]) => {
  return await Promise.all(
    items.map(async message => {
      const height = await calculateTextHeight({
        text: message.formatted_text,
        blocks: message.blocks,
      });
      return {
        ...message,
        height,
      };
    }),
  );
};
