import { ReactNode, forwardRef, useMemo } from 'react';

import { useTestIdAttribute } from '../../hooks/useTestIdAttribute';
import { CommonProps } from '../../types';
import { assertEmptyObject } from '../../utils/assertEmptyObject';
import { assertUnreachable } from '../../utils/assertUnreachable';

import { TextVariant } from './constants';
import {
  StyledBody,
  StyledCaption,
  StyledCardSubtitle,
  StyledCardTitle,
  StyledCode,
  StyledGroupHeader,
  StyledSectionHeadline,
} from './styled';

/** Props for the {@link Text} component. */
export interface TextProps extends CommonProps {
  /** Variant of text rendering */
  variant: TextVariant;
  /** HTML tag name that should be used for text rendering */
  as?: keyof JSX.IntrinsicElements;
  children?: ReactNode;
}

/** Text component is used to display text. */
export const Text = forwardRef<HTMLElement, TextProps>((props, ref) => {
  const { variant, as, children, className, testId, ariaDescribedBy, ...rest } = props;
  assertEmptyObject(rest);

  const testIdAttribute = useTestIdAttribute();

  const StyledRenderComponent = useMemo(() => {
    switch (variant) {
      case TextVariant.SectionHeadline:
        return StyledSectionHeadline;
      case TextVariant.CardTitle:
        return StyledCardTitle;
      case TextVariant.CardSubtitle:
        return StyledCardSubtitle;
      case TextVariant.GroupHeader:
        return StyledGroupHeader;
      case TextVariant.Body:
        return StyledBody;
      case TextVariant.Caption:
        return StyledCaption;
      case TextVariant.Code:
        return StyledCode;
      /* istanbul ignore next */
      default:
        assertUnreachable(variant);
    }
  }, [variant]);

  return (
    <StyledRenderComponent
      ref={ref as any}
      aria-describedby={ariaDescribedBy}
      as={as as any}
      className={className}
      {...{ [testIdAttribute]: testId }}
    >
      {children}
    </StyledRenderComponent>
  );
});
