import { forwardRef } from "react";

import { omit } from "../../utils";
import { StyledAvatar, AvatarImage, AvatarFallback } from "./avatar.styled";

import type { WithTestId } from "../../types";

interface ImageProps {
  /**
   * The image to render. By default it will only render when it has loaded.
   */
  image: string;
  /**
   * The alt text alongside the image.
   */
  alt: string;
  /**
   * Useful for delaying rendering so it only appears for those with slower connections.
   */
  fallbackDelayMs?: number;
  /**
   * [Radix Documentation](https://www.radix-ui.com/primitives/docs/components/avatar#image)
   */
  imageProps?: Omit<React.ComponentProps<typeof AvatarImage>, "src" | "alt">;
}

interface NoImageProps {
  image?: never;
  alt?: never;
}

interface AvatarBaseProps extends WithTestId<React.ComponentProps<typeof StyledAvatar>> {
  /**
   * Change the default rendered element for the one passed as a child, merging their props and behavior.
   */
  asChild?: boolean;
  /**
   * The text that renders when the image hasn't loaded. This means whilst it's loading, or if there was an error. Only the first two characters will be rendered to fit in the container.
   */
  fallback: React.ReactNode;
  /**
   * [Radix Documentation](https://www.radix-ui.com/primitives/docs/components/avatar#fallback)
   */
  fallbackProps?: Omit<React.ComponentProps<typeof AvatarFallback>, "delayMs">;
}

type AvatarProps = (AvatarBaseProps & ImageProps) | (AvatarBaseProps & NoImageProps);

const DEFAULT_FALLBACK_DELAY = 600;

/**
 * Based on [Radix Primitives Avatar](https://www.radix-ui.com/primitives/docs/components/avatar).
 *
 * A circular image element with fallback. Primarly used for representing different entities.
 */
export const Avatar = forwardRef<HTMLSpanElement, AvatarProps>(({ fallback, ...props }, ref) => {
  const fb = typeof fallback === "string" ? fallback.substring(0, 2) : fallback;

  if (props.image) {
    const { image, alt, fallbackDelayMs, imageProps, fallbackProps, ...rest } = props;

    return (
      <StyledAvatar ref={ref} {...rest}>
        <AvatarImage src={image} alt={alt} {...omit(imageProps, ["src", "alt"])} />
        <AvatarFallback
          delayMs={fallbackDelayMs ?? DEFAULT_FALLBACK_DELAY}
          {...omit(fallbackProps, ["delayMs"])}
        >
          {fb}
        </AvatarFallback>
      </StyledAvatar>
    );
  }
  const { fallbackProps, ...rest } = props;
  return (
    <StyledAvatar ref={ref} {...rest}>
      <AvatarFallback delayMs={0} {...omit(fallbackProps, ["delayMs"])}>
        {fb}
      </AvatarFallback>
    </StyledAvatar>
  );
});

Avatar.displayName = "Avatar";

export const AvatarSelector = StyledAvatar.toString();
export const AvatarImageSelector = AvatarImage.toString();
export const AvatarFallbackSelector = AvatarFallback.toString();
