import React, { ReactNode, useCallback, useRef, useState } from "react";
import classNames from "classnames";
import { Transition } from "react-spring/renderprops";
import useComponentSize from "@rehooks/component-size";
import { useId } from "@reach/auto-id";

import { BoxProps, Box } from "../Box";
import { Flex } from "../Flex";
import { Heading } from "../Heading";
import { Icon, ICON_TYPE } from "../Icon";
import { bemHOF } from "../../utilities/bem";
import { useDisclosure } from "../../hooks";

const cn = bemHOF("Accordion");

export interface AccordionProps extends BoxProps {
  /**
   * The displayed text of the `Accordion` when closed
   */
  title: string;

  /**
   * If true, this shows a green indicator next to the title
   */
  isActive?: boolean;

  /**
   * CSS value to define a max height of the opened Accordion content.
   * If the children exceed the height of the container, it will scroll.
   */
  maxHeight?: string;

  /**
   * Whether or not the accordion item is open / visible.
   */
  defaultIsOpen?: boolean;

  /**
   * Add an icon or integration logo to the left of the header title
   */
  iconSlot?: ReactNode;

  /**
   * Removes padding from the Accordion's content
   */
  removePadding?: ReactNode;
}

export const AccordionGroup = ({
  children,
  className,
  ...rest
}: {
  children: ReactNode;
  className?: string;
}) => (
  <Flex className={classNames("flex-col", className)} {...rest}>
    {children}
  </Flex>
);

export const Accordion = ({
  title,
  isActive,
  maxHeight,
  children,
  defaultIsOpen = false,
  iconSlot,
  className,
  removePadding,
  ...rest
}: AccordionProps) => {
  const { isOpen, onToggle } = useDisclosure(defaultIsOpen);
  const buttonRef = useRef<HTMLButtonElement>(null);

  const [childrenRef, setChildrenRef] = useState<
    React.MutableRefObject<null | HTMLDivElement>
  >({ current: null });
  const onRefChange = useCallback((node) => {
    if (node !== null) {
      setChildrenRef({ current: node });
    }
  }, []);

  const { height } = useComponentSize(childrenRef);
  const heightObj = { height };

  const id = `accordion:${useId()}`;
  const buttonId = `${id}-button`;
  const contentId = `${id}-content`;

  const [enableTransition, setEnableTransition] = useState(!defaultIsOpen);

  const handleItemClick = () => {
    if (isOpen && buttonRef.current !== null) {
      buttonRef.current.blur();
    }
    onToggle();
  };

  return (
    <Box
      className={classNames(cn(), className, {
        [cn({ m: "isOpen" })]: isOpen,
      })}
      {...rest}
    >
      <Heading.H5>
        <button
          type="button"
          onClick={handleItemClick}
          className={classNames(cn({ e: "button" }), {
            [cn({ e: "button", m: "isActive" })]: isActive,
          })}
          ref={buttonRef}
          id={buttonId}
          aria-controls={contentId}
          aria-expanded={isOpen}
          aria-disabled="false"
        >
          <Flex className="items-center" as="span">
            {iconSlot && (
              <Box className={classNames(cn({ e: "icon" }))} as="span">
                {iconSlot}
              </Box>
            )}
            {title}
          </Flex>
          <Icon
            icon={ICON_TYPE.CHEVRON_DOWN}
            className={cn({ e: "chevronIcon" })}
          />
        </button>
      </Heading.H5>
      <Transition
        items={isOpen}
        from={{ height: 0 }}
        enter={{ height }}
        leave={{ height: 0 }}
        update={heightObj}
        onRest={() => {
          if (defaultIsOpen && isOpen) {
            setEnableTransition(true);
          }
        }}
        immediate={!enableTransition}
      >
        {(show) => {
          return (
            show &&
            ((props) => (
              <div style={props} className={cn({ e: "content" })}>
                <Box
                  ref={onRefChange}
                  className={classNames(
                    cn({ e: "children" }),
                    !removePadding && cn({ e: "children", m: "padded" }),
                  )}
                  style={maxHeight ? { maxHeight } : undefined}
                  id={contentId}
                  aria-labelledby={buttonId}
                >
                  {children}
                </Box>
              </div>
            ))
          );
        }}
      </Transition>
    </Box>
  );
};

Accordion.Group = AccordionGroup;
