import { memo, useState } from 'react';

import { useI18n } from '~/src/lib/i18n';
import { raf } from '~/src/lib/utils/raf';
import Box from '../Box';
import Clickable from '../Clickable';
import IsOverflowing from '../IsOverflowing';
import { useScroller } from '../Scroller2';
import Text from '../Text';
import FormattedText from './FormattedText';

interface CollapsableTextProps {
  text: string;
  textSize: number | string;
  initialMaxLines: number;
  lineHeight: number | string;
  testId: string;
  margin: string;
  fontFamily?: string;
  withLinkify: boolean;
  withShowMore: boolean;
  color?: string;
}

/**
 * COMPLEX:
 *
 * We're using IntersectionObserver to detect if/when the parent container
 * has overflowed and we should show the 'Show more' button underneath.
 *
 * When using line-clamp the <p> doesn't ever overflow so we changed
 * to instead of observe the overflow of a tiny 1x1 <span>. When the
 * inline span is not visible then we know that there is text overflowing.
 */
const CollapsableText = memo<CollapsableTextProps>(
  ({
    text,
    textSize,
    initialMaxLines,
    lineHeight,
    testId,
    margin,
    withLinkify,
    withShowMore,
    color,
    fontFamily,
  }) => {
    const { t } = useI18n();
    const [isExpanded, setIsExpanded] = useState(withShowMore ? false : true);
    const scroller = useScroller();
    const marginBottom = !isExpanded ? '2rem' : 0;

    const maxHeight = !isExpanded
      ? `calc(${initialMaxLines} * ${lineHeight})`
      : 'none';

    return (
      <Box margin={margin} data-testid={testId} style={{ color }}>
        <IsOverflowing
          fallbackValue={!isExpanded}
          render={({ rootRef, isOverflowing, targetRef }) => (
            <>
              <div
                ref={rootRef as any}
                style={{
                  overflow: 'hidden',
                  maxHeight,
                  marginBottom,
                }}
              >
                <FormattedText
                  text={text}
                  withLinkify={withLinkify}
                  lineClamp={!isExpanded ? initialMaxLines : undefined}
                  size={textSize}
                  lineHeight={lineHeight}
                  family={fontFamily}
                >
                  <span
                    ref={targetRef}
                    style={{
                      height: 1,
                      width: 1,
                    }}
                  />
                </FormattedText>
              </div>
              {!isExpanded && (
                <Clickable
                  pointerEvents={isOverflowing ? 'all' : 'none'}
                  onClick={() => {
                    const prevScrollTop = scroller?.getScrollTop();

                    setIsExpanded(!isExpanded);

                    if (!prevScrollTop) return;

                    // immediately restore scroll position to avoid
                    // the layout shift changing the viewport context
                    raf(() => {
                      scroller?.setScrollTop(prevScrollTop);
                    });
                  }}
                >
                  <Text
                    size={textSize}
                    isBold
                    centered
                    family={fontFamily}
                    opacity={isOverflowing ? 1 : 0}
                  >
                    {t('app.actions.showMore')}
                  </Text>
                </Clickable>
              )}
            </>
          )}
        />
      </Box>
    );
  }
);

export default CollapsableText;
