import { type FC, type ReactNode } from 'react';
import Linkify from 'react-linkify';
import { Link as RouterLink } from 'react-router-dom';

import { COMPONENT_TYPES } from 'tag-users/constants';
import Box from '@material-hu/mui/Box';
import Link from '@material-hu/mui/Link';
import Typography from '@material-hu/mui/Typography';

import { profileRoutes } from 'src/pages/dashboard/profile/routes';
import { findFirstMatch, withoutNonPrintingCharacter } from 'src/utils/feed';

type PostComponent = {
  type: string;
  text: string;
  userId?: number;
};

type TextTypeProps = {
  text: string;
  i: number;
  isMultiLines: boolean;
  split: boolean;
  hasToBeSplit?: boolean;
  maxCharacters?: number;
  maxNewLines?: number;
  lastCount: number;
  matchText?: string;
  userId?: number;
};

const showMatch = (text: string, matchText?: string): ReactNode => {
  if (!matchText) return text;

  const matchInfo = findFirstMatch(text, matchText);

  if (!matchInfo) return text;

  const { matchIndex, matchLength } = matchInfo;

  return (
    <>
      {text.slice(0, matchIndex)}
      <Box
        component="span"
        sx={{ backgroundColor: '#f9e8b4' }}
      >
        {text.slice(matchIndex, matchIndex + matchLength)}
      </Box>
      {text.slice(matchIndex + matchLength)}
    </>
  );
};

type TextTypeHandler = (props: TextTypeProps) => ReactNode;

const getTextType: Record<string, TextTypeHandler> = {
  [COMPONENT_TYPES.NORMAL_TEXT]: ({
    text,
    i,
    isMultiLines,
    split,
    hasToBeSplit,
    maxCharacters = 0,
    maxNewLines = 0,
    lastCount,
    matchText,
  }: TextTypeProps) => {
    const getSplitedText = () => {
      if (!text) return text;

      const textFormatted = withoutNonPrintingCharacter(text);

      if (isMultiLines) {
        return split
          ? textFormatted.slice(0, maxCharacters - lastCount)
          : `${textFormatted} `;
      }

      return !hasToBeSplit
        ? textFormatted
        : textFormatted
            .split('\n')
            .slice(0, maxNewLines + 1)
            .join('\n')
            .slice(0, maxCharacters);
    };

    const handleClickLink = (event: React.MouseEvent<HTMLAnchorElement>) => {
      event.stopPropagation();
    };

    const isBreakLine = !text && i === 0 && !isMultiLines;

    return (
      <Linkify
        key={i}
        componentDecorator={(
          decoratedHref: string,
          decoratedText: string,
          key: number,
        ) => (
          <a
            target="_blank"
            rel="noreferrer"
            href={decoratedHref}
            key={key}
            onClick={handleClickLink}
          >
            {decoratedText}
          </a>
        )}
      >
        <Typography
          color="textPrimary"
          variant="body1"
          sx={{
            whiteSpace: 'pre-wrap',
            mb: 2,
            display: 'inline',
            wordWrap: 'break-word',
            overflowWrap: 'break-word',
            WebkitHyphens: 'auto',
            MozHyphens: 'auto',
            msHyphens: 'auto',
            hyphens: 'auto',
          }}
        >
          {showMatch(getSplitedText(), matchText || '')}
          {isBreakLine && '\n'}
        </Typography>
      </Linkify>
    );
  },
  [COMPONENT_TYPES.PROFILE_LINK]: ({
    userId,
    text,
    split,
    lastCount,
    maxCharacters = 0,
    matchText,
  }: TextTypeProps) => {
    const getSplitedText = () =>
      split ? text.slice(0, maxCharacters - lastCount) : text;

    return (
      <Link
        color="textPrimary"
        component={RouterLink}
        to={profileRoutes.profile(userId!)}
        variant="subtitle2"
        key={text}
        sx={{
          mb: 1,
          mr: 0.5,
          color: theme => theme.palette.new.text.neutral.brand,
          backgroundColor: theme => theme.palette.new.background.elements.brand,
        }}
      >
        {showMatch(getSplitedText(), matchText || '')}
      </Link>
    );
  },
};

export type ParagraphsTextTypeProps = {
  paragraph: PostComponent[];
  hasToBeSplit?: boolean;
  maxCharacters?: number;
  maxNewLines?: number;
  matchText?: string;
};

export const ParagraphsTextType: FC<ParagraphsTextTypeProps> = props => {
  const {
    paragraph,
    hasToBeSplit,
    maxCharacters = 0,
    maxNewLines = 0,
    matchText = '',
  } = props;

  if (paragraph.length === 1) {
    return paragraph.map((postComponents: PostComponent, i: number) => {
      const isMultiLines = false;
      const handler = getTextType[postComponents.type];
      if (!handler) return null;
      return handler({
        ...postComponents,
        i,
        isMultiLines,
        hasToBeSplit,
        maxCharacters,
        maxNewLines,
        matchText,
        split: false,
        lastCount: 0,
      });
    });
  }

  let split = false;
  let preCount = 0;
  let counter = 0;
  let lastCount = 0;

  return paragraph.map((postComponents: PostComponent, i: number) => {
    const isMultiLines = true;

    if (postComponents.text.length !== 0) {
      counter += postComponents.text.length + 1;
    }

    if (hasToBeSplit) {
      if (counter > maxCharacters) {
        split = true;
      }
    }

    lastCount = preCount;
    preCount = counter;

    if (lastCount < maxCharacters || !hasToBeSplit) {
      const handler = getTextType[postComponents.type];
      if (!handler) return null;
      return handler({
        ...postComponents,
        i,
        isMultiLines,
        split,
        lastCount,
        maxCharacters,
        matchText,
      });
    }
    return null;
  });
};

export default ParagraphsTextType;
