// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import Measure from 'react-measure';
import React, { useEffect, useRef, useState } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import classNames from 'classnames';

import type { LocalizerType, RenderTextCallbackType } from '../types/Util';
import type { TextAttachmentType } from '../types/Attachment';
import { AddNewLines } from './conversation/AddNewLines';
import { Emojify } from './conversation/Emojify';
import { StoryLinkPreview } from './StoryLinkPreview';
import { TextAttachmentStyleType } from '../types/Attachment';
import { count } from '../util/grapheme';
import { getDomain } from '../types/LinkPreview';
import { getFontNameByTextScript } from '../util/getFontNameByTextScript';
import {
  COLOR_WHITE_INT,
  getHexFromNumber,
  getBackgroundColor,
} from '../util/getStoryBackground';

const renderNewLines: RenderTextCallbackType = ({
  text: textWithNewLines,
  key,
}) => {
  return <AddNewLines key={key} text={textWithNewLines} />;
};

const CHAR_LIMIT_TEXT_LARGE = 50;
const CHAR_LIMIT_TEXT_MEDIUM = 200;
const FONT_SIZE_LARGE = 59;
const FONT_SIZE_MEDIUM = 42;
const FONT_SIZE_SMALL = 32;

enum TextSize {
  Small,
  Medium,
  Large,
}

export type PropsType = {
  disableLinkPreviewPopup?: boolean;
  i18n: LocalizerType;
  isEditingText?: boolean;
  isThumbnail?: boolean;
  onChange?: (text: string) => unknown;
  onClick?: () => unknown;
  onRemoveLinkPreview?: () => unknown;
  textAttachment: TextAttachmentType;
};

function getTextSize(text: string): TextSize {
  const length = count(text);

  if (length < CHAR_LIMIT_TEXT_LARGE) {
    return TextSize.Large;
  }

  if (length < CHAR_LIMIT_TEXT_MEDIUM) {
    return TextSize.Medium;
  }

  return TextSize.Small;
}

function getFont(
  text: string,
  textSize: TextSize,
  textStyle?: TextAttachmentStyleType | null,
  i18n?: LocalizerType
): string {
  const textStyleIndex = Number(textStyle) || 0;
  const fontName = getFontNameByTextScript(text, textStyleIndex, i18n);

  let fontSize = FONT_SIZE_SMALL;
  switch (textSize) {
    case TextSize.Large:
      fontSize = FONT_SIZE_LARGE;
      break;
    case TextSize.Medium:
      fontSize = FONT_SIZE_MEDIUM;
      break;
    default:
      fontSize = FONT_SIZE_SMALL;
  }

  const fontWeight = textStyle === TextAttachmentStyleType.BOLD ? 'bold ' : '';

  return `${fontWeight}${fontSize}px ${fontName}`;
}

function getTextStyles(
  textContent: string,
  textForegroundColor?: number | null,
  textStyle?: TextAttachmentStyleType | null,
  i18n?: LocalizerType
): { color: string; font: string; textAlign: 'left' | 'center' } {
  return {
    color: getHexFromNumber(textForegroundColor || COLOR_WHITE_INT),
    font: getFont(textContent, getTextSize(textContent), textStyle, i18n),
    textAlign: getTextSize(textContent) === TextSize.Small ? 'left' : 'center',
  };
}

export function TextAttachment({
  disableLinkPreviewPopup,
  i18n,
  isEditingText,
  isThumbnail,
  onChange,
  onClick,
  onRemoveLinkPreview,
  textAttachment,
}: PropsType): JSX.Element | null {
  const linkPreview = useRef<HTMLDivElement | null>(null);
  const [linkPreviewOffsetTop, setLinkPreviewOffsetTop] = useState<
    number | undefined
  >();

  const textContent = textAttachment.text || '';

  const textEditorRef = useRef<HTMLTextAreaElement | null>(null);

  useEffect(() => {
    const node = textEditorRef.current;
    if (!node) {
      return;
    }

    node.focus();
    node.setSelectionRange(node.value.length, node.value.length);
  }, [isEditingText]);

  const storyBackgroundColor = {
    background: getBackgroundColor(textAttachment),
  };

  return (
    <Measure bounds>
      {({ contentRect, measureRef }) => {
        const scaleFactor = (contentRect.bounds?.height || 1) / 1280;

        return (
          // eslint-disable-next-line jsx-a11y/no-static-element-interactions
          <div
            className="TextAttachment"
            onClick={() => {
              if (linkPreviewOffsetTop) {
                setLinkPreviewOffsetTop(undefined);
              }
              onClick?.();
            }}
            onKeyUp={ev => {
              if (ev.key === 'Escape' && linkPreviewOffsetTop) {
                setLinkPreviewOffsetTop(undefined);
              }
            }}
            ref={measureRef}
            style={isThumbnail ? storyBackgroundColor : undefined}
          >
            {/*
            The tooltip must be outside of the scaled area, as it should not scale with
            the story, but it must be positioned using the scaled offset
            */}
            {textAttachment.preview &&
              textAttachment.preview.url &&
              linkPreviewOffsetTop &&
              !isThumbnail && (
                <a
                  className="TextAttachment__preview__tooltip"
                  href={textAttachment.preview.url}
                  rel="noreferrer"
                  style={{
                    top: linkPreviewOffsetTop * scaleFactor - 89, // minus height of tooltip and some spacing
                  }}
                  target="_blank"
                >
                  <div>
                    <div className="TextAttachment__preview__tooltip__title">
                      {i18n('TextAttachment__preview__link')}
                    </div>
                    <div className="TextAttachment__preview__tooltip__url">
                      {textAttachment.preview.url}
                    </div>
                  </div>
                  <div className="TextAttachment__preview__tooltip__arrow" />
                </a>
              )}
            <div
              className="TextAttachment__story"
              style={{
                ...(isThumbnail ? {} : storyBackgroundColor),
                transform: `scale(${scaleFactor})`,
              }}
            >
              {(textContent || onChange) && (
                <div
                  className={classNames('TextAttachment__text', {
                    'TextAttachment__text--with-bg': Boolean(
                      textAttachment.textBackgroundColor
                    ),
                  })}
                  style={{
                    backgroundColor: textAttachment.textBackgroundColor
                      ? getHexFromNumber(textAttachment.textBackgroundColor)
                      : 'transparent',
                  }}
                >
                  {onChange ? (
                    <TextareaAutosize
                      className="TextAttachment__text__container TextAttachment__text__textarea"
                      disabled={!isEditingText}
                      onChange={ev => onChange(ev.currentTarget.value)}
                      placeholder={i18n('TextAttachment__placeholder')}
                      ref={textEditorRef}
                      style={getTextStyles(
                        textContent,
                        textAttachment.textForegroundColor,
                        textAttachment.textStyle,
                        i18n
                      )}
                      value={textContent}
                    />
                  ) : (
                    <div
                      className="TextAttachment__text__container"
                      style={getTextStyles(
                        textContent,
                        textAttachment.textForegroundColor,
                        textAttachment.textStyle,
                        i18n
                      )}
                    >
                      <Emojify
                        text={textContent}
                        renderNonEmoji={renderNewLines}
                      />
                    </div>
                  )}
                </div>
              )}
              {textAttachment.preview && textAttachment.preview.url && (
                <div
                  className={classNames('TextAttachment__preview-container', {
                    'TextAttachment__preview-container--large': Boolean(
                      textAttachment.preview.title
                    ),
                  })}
                  ref={linkPreview}
                  onFocus={() => {
                    if (!disableLinkPreviewPopup) {
                      setLinkPreviewOffsetTop(linkPreview?.current?.offsetTop);
                    }
                  }}
                  onMouseOver={() => {
                    if (!disableLinkPreviewPopup) {
                      setLinkPreviewOffsetTop(linkPreview?.current?.offsetTop);
                    }
                  }}
                >
                  {onRemoveLinkPreview && (
                    <div className="TextAttachment__preview__remove">
                      <button
                        aria-label={i18n('Keyboard--remove-draft-link-preview')}
                        type="button"
                        onClick={onRemoveLinkPreview}
                      />
                    </div>
                  )}
                  <StoryLinkPreview
                    {...textAttachment.preview}
                    domain={getDomain(String(textAttachment.preview.url))}
                    forceCompactMode={
                      getTextSize(textContent) !== TextSize.Large
                    }
                    i18n={i18n}
                    title={textAttachment.preview.title || undefined}
                    url={textAttachment.preview.url}
                  />
                </div>
              )}
            </div>
          </div>
        );
      }}
    </Measure>
  );
}
