import { forwardRef, useCallback } from "react";
import { createPortal } from "react-dom";
import { Text } from "../text";
import { IconButton } from "../icon-button";
import { Icons } from "../icons";
import {
  StyledRoot,
  StyledTitle,
  StyledDescription,
  StyledClose,
  StyledIconContainer,
  StyledTextTitle,
} from "./toast.styled";
import { useToast } from "./toast-provider";

import type { WithTestId } from "../../types";
import type * as Stitches from "@stitches/react";

type RootVariants = Stitches.VariantProps<typeof StyledRoot>;

interface Props {
  /**
   * The title of the toast.
   */
  title: string | React.ReactElement<{ children: string }> | null | undefined;
  /**
   * The description of the toast.
   */
  description?: string | React.ReactElement<{ children: string }>;
  /**
   * The icon displayed on the left.
   */
  icon?: React.ReactElement;
  /**
   * Aria label for the close button.
   */
  closeLabel: string;
  /**
   * The time in milliseconds that should elapse before automatically closing the toast.
   */
  duration?: number;
  /**
   * The style variant of the toast.
   */
  variant?: RootVariants["variant"];
  /**
   * The id of the toast, used to remove it from the dom
   */
  id: number;
  /**
   * Props to be passed to the __StyledRoot__ component.
   */
  rootProps?: Omit<React.ComponentProps<typeof StyledRoot>, "duration">;
  /**
   * Props to be passed to the __StyledTitle__ component.
   */
  titleProps?: React.ComponentProps<typeof StyledTitle>;
  /**
   * Props to be passed to the __StyledDescription__ component.
   */
  descriptionProps?: React.ComponentProps<typeof StyledDescription>;
  /**
   * Props to be passed to the __StyledClose__ component.
   */
  closeProps?: Omit<React.ComponentProps<typeof StyledClose>, "asChild">;

  /**
   * Should content be rendered in a portal? You can pass DOM element to render content in it or true if you want to
   * render content in the body (this is default because of possible iOS bugs when using Toast from within ScrollArea).
   * Pass false to disable portal and render content in place.
   */
  portal?: boolean | HTMLElement;
}

export type ToastProps = WithTestId<Props>;

/**
 * Use this to display small piece of information.
 */

export const Toast = forwardRef<HTMLLIElement, ToastProps>(
  // eslint-disable-next-line max-lines-per-function
  (
    {
      title,
      description,
      closeLabel,
      duration = 5000,
      rootProps,
      titleProps,
      descriptionProps,
      closeProps,
      variant,
      icon,
      portal = true,
      id,
      ...props
    },
    ref
  ) => {
    const { removeToastById } = useToast();
    const handleOnOpenChange = useCallback(
      (open: boolean) => {
        if (!open) {
          removeToastById(id);
        }
      },
      [removeToastById, id]
    );
    const iconVariant = variant === "success" || variant === "warning" ? "ghost-dark" : "ghost";

    const content = (
      <>
        <StyledRoot
          ref={ref}
          onOpenChange={handleOnOpenChange}
          {...rootProps}
          variant={variant}
          duration={duration}
          withDescription={Boolean(description)}
          withIcon={Boolean(icon)}
          {...props}
        >
          {icon && <StyledIconContainer>{icon}</StyledIconContainer>}
          <StyledTitle {...titleProps}>
            {typeof title === "string" ? <StyledTextTitle>{title}</StyledTextTitle> : title}
          </StyledTitle>
          {description && (
            <StyledDescription {...descriptionProps}>
              {typeof description === "string" ? (
                <Text variant="body5">{description}</Text>
              ) : (
                description
              )}
            </StyledDescription>
          )}
          <StyledClose {...closeProps} asChild aria-label={closeLabel}>
            <IconButton Icon={Icons.Close} variant={iconVariant} />
          </StyledClose>
        </StyledRoot>
      </>
    );

    if (portal) {
      return createPortal(content, typeof portal === "boolean" ? document.body : portal);
    }
    return content;
  }
);

Toast.displayName = "Toast";
