import React, { useCallback } from "react";
import { IconButton } from "../icon-button";
import { Text } from "../text";

import { Icons } from "../icons";
import {
  StyledDialog,
  StyledTrigger,
  StyledClose,
  StyledContent,
  StyledOverlay,
  StyledPortal,
  StyledTitle,
  StyledDescription,
  StyledHeader,
  Content,
} from "./dialog.styled";
import type { TextProps } from "../text";

type StitchesDialogProps = React.ComponentProps<typeof StyledDialog>;

export interface DialogProps {
  /**
   * The content of the dialog.
   */
  children: React.ReactNode;
  /**
   * Whenever dialog should be forcibly opened or closed. Skip to let the dialog manage its own state.
   */
  open?: StitchesDialogProps["open"];
  /**
   * Callback fired when the dialog open state changes.
   */
  onOpenChange?: StitchesDialogProps["onOpenChange"];
  /**
   * Defines the placement of the dialog.
   */
  side?: React.ComponentProps<typeof StyledContent>["side"];
  /**
   * Optional ReactNode to be placed at the start of the header
   */
  startItem?: React.ReactNode;
  /**
   * The title of the dialog, rendered on the top.
   */
  title?: string | undefined;
  /**
   * If `true` will horizontally center title
   */
  centerTitle?: boolean;
  /**
   * The description of the dialog, rendered below the title.
   */
  description?: string;
  /**
   * Whether the default close button should be rendered or not.
   */
  showCloseButton?: boolean;
  /**
   * Whether the default close button should be rendered or not.
   */
  showHeader?: boolean;
  /**
   * Whether the overlay should be opaque or not.
   */
  hasOpaqueOverlay?: boolean;

  /**
   * The trigger of the dialog. It will be rendered in place where the <Dialog> element is rendered.
   */
  trigger?: React.ReactNode;
  /**
   * Props to be passed to the __StyledDialog__
   */
  rootProps?: Omit<React.ComponentProps<typeof StyledDialog>, "open" | "onOpenChange">;
  /**
   * Props to be passed to the __StyledTrigger__
   */
  triggerProps?: React.ComponentProps<typeof StyledTrigger>;
  /**
   * Props to be passed to the __StyledPortal__
   */
  portalProps?: React.ComponentProps<typeof StyledPortal>;
  /**
   * Props to be passed to the __StyledOverlay__
   */
  overlayProps?: Omit<React.ComponentProps<typeof StyledOverlay>, "opaque">;
  /**
   * Props to be passed to the __StyledContent__
   */
  contentProps?: Omit<React.ComponentProps<typeof Content>, "side">;
  /**
   * Props to be passed to the __StyledTitle__
   */
  titleProps?: Omit<React.ComponentProps<typeof StyledTitle>, "asChild">;
  /**
   * Props to be passed to the __<Text>__ inside the __StyledTitle__
   */
  titleTextProps?: Omit<TextProps, "children">;
  /**
   * Props to be passed to the __StyledDescription__
   */
  descriptionProps?: React.ComponentProps<typeof StyledDescription>;
  /**
   * Props to be passed to the __StyledClose__
   */
  closeProps?: Omit<React.ComponentProps<typeof StyledClose>, "hide" | "asChild">;
  /**
   * Control over the close icon button variant
   */
  closeButtonVariant?: React.ComponentProps<typeof IconButton>["variant"];
  /**
   * Control over the close icon button size
   */
  closeButtonSize?: React.ComponentProps<typeof IconButton>["size"];
  /**
   * Whether the dialog should be closed when clicking outside of it.
   */
  closeOnClickOutside?: boolean;
  /**
   * Content rendered outside the visual container of the dialog
   */
  nonTransformedContent?: React.ReactNode;
}
// eslint-disable-next-line max-lines-per-function
export const Dialog: React.FC<DialogProps> = ({
  open,
  onOpenChange,
  children,
  hasOpaqueOverlay = true,
  side = "center",
  startItem,
  title,
  centerTitle,
  description,
  showCloseButton = false,
  showHeader = true,
  trigger,
  rootProps,
  triggerProps,
  portalProps,
  overlayProps,
  contentProps,
  titleProps,
  titleTextProps,
  descriptionProps,
  closeProps,
  closeButtonVariant = "secondary",
  closeButtonSize = "m",
  closeOnClickOutside = true,
  nonTransformedContent,
  ...htmlElementAttributes
}) => {
  const { onPointerDownOutside, css, ...restContentProps } = contentProps ?? {};

  const handlePointerDownOutside = useCallback(
    (
      e: Parameters<NonNullable<React.ComponentProps<typeof Content>["onPointerDownOutside"]>>[0]
    ) => {
      onPointerDownOutside?.(e);
      if (!closeOnClickOutside) {
        e.preventDefault();
      }
    },
    [closeOnClickOutside, onPointerDownOutside]
  );

  return (
    <StyledDialog
      {...rootProps}
      {...(typeof open === "boolean" && { open })}
      onOpenChange={onOpenChange}
    >
      {trigger && <StyledTrigger {...triggerProps}>{trigger}</StyledTrigger>}
      <StyledPortal {...portalProps}>
        <StyledOverlay {...overlayProps} opaque={hasOpaqueOverlay} />
        <Content {...restContentProps} onPointerDownOutside={handlePointerDownOutside}>
          <StyledContent css={css} side={side} {...htmlElementAttributes}>
            {showHeader && (
              <StyledHeader>
                {startItem}
                {title && (
                  <StyledTitle {...titleProps} {...(centerTitle && { centerTitle })} asChild>
                    <Text variant="h4" {...titleTextProps}>
                      {title}
                    </Text>
                  </StyledTitle>
                )}

                {showCloseButton && (
                  <StyledClose {...closeProps} asChild>
                    <IconButton
                      variant={closeButtonVariant}
                      Icon={Icons.Close}
                      size={closeButtonSize}
                      aria-label="Close"
                    />
                  </StyledClose>
                )}
              </StyledHeader>
            )}
            {description && (
              <StyledDescription {...descriptionProps}>
                <Text>{description}</Text>
              </StyledDescription>
            )}
            {children}
          </StyledContent>
          {nonTransformedContent}
        </Content>
      </StyledPortal>
    </StyledDialog>
  );
};
