import { useMemo } from 'react';

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

import { ReactComponent as SilhouetteSvg } from './assets/silhouette.svg';
import { AvatarColor, AvatarSize } from './constants';
import { StyledAvatar, StyledImg } from './styled';

/** Props for {@link Avatar} */
export interface AvatarProps extends CommonProps {
  /**
   * This attribute defines the alternative text describing the image.
   * Can be an empty string, but can't be undefined (see https://webaim.org/techniques/alttext/)
   */
  alt: string;
  /**
   * Color of avatar.
   *
   * @default {@link AvatarColor.Gray}
   */
  color?: AvatarColor;
  /**
   * Size of avatar.
   *
   * @default {@link AvatarSize.Default}
   */
  size?: AvatarSize;
  /**
   * URL of the avatar image which falls back to initials if the image fails to load.
   */
  src?: string;
  /**
   * Initials of person to display in case of no image provided.
   * If not provided, silhouette variant will be used as fallback.
   */
  initials?: string;
}

/**
 * Avatars are used to display thumbnails of individuals or companies in the interface.
 * If color and size are not specified the Avatar has a gray background and default size 64x64px.
 *
 * <Story id="components-avatar--default" />
 *
 * ### Colors
 *
 * Optionally you can set a specific color bypassing the `color` props with one of the following values:
 * `gray`, `blue`, `green`, `orange`, `red`
 *
 * <Story id="components-avatar--avatar-colors" />
 *
 * ### Sizes
 *
 * Optionally you can set a specific size bypassing the `size` props with one of the following values:
 * `xsmall`, `small`, `default`, `large`
 *
 * <Story id="components-avatar--avatar-sizes" />
 *
 * ### Initials
 *
 * If the `initials` prop is provided, instead of showing default svg with silhouette, initials will be shown.
 *
 * <Story id="components-avatar--avatar-initials" />
 *
 * ### Images
 *
 * If the image source is provided by `src` prop to the Avatar, the component will use that image as a background.
 *
 * <Story id="components-avatar--avatar-image" />
 */
export function Avatar(props: AvatarProps) {
  const {
    size = AvatarSize.Default,
    color = AvatarColor.Gray,
    alt,
    src,
    initials,
    className,
    testId,
    ariaDescribedBy,
    ...rest
  } = props;
  assertEmptyObject(rest);

  const width = useMemo(() => {
    switch (size) {
      case AvatarSize.XSmall:
        return 22;
      case AvatarSize.Small:
        return 32;
      case AvatarSize.Default:
        return 64;
      case AvatarSize.Large:
        return 120;
      /* istanbul ignore next */
      default:
        assertUnreachable(size);
    }
  }, [size]);

  const testIdAttribute = useTestIdAttribute();
  const image = src && <StyledImg role="presentation" src={src} />;
  const imageFallback = initials ? <span aria-hidden>{initials}</span> : <SilhouetteSvg width={width} />;

  return (
    <StyledAvatar
      $color={color}
      $hasInitials={!!initials}
      $size={size}
      $width={width}
      aria-describedby={ariaDescribedBy}
      aria-label={alt}
      className={className}
      role="img"
      {...{ [testIdAttribute]: testId }}
    >
      {image || imageFallback}
    </StyledAvatar>
  );
}
