import React, { useEffect, memo, useRef } from 'react';
import styled, { css } from 'styled-components';

import type { PropsWithChildren, ReactNode } from 'react';

import { useControl, useOutsideClick } from '@redocly/theme/core/hooks';

export type TooltipProps = {
  tip: string | ReactNode;
  isOpen?: boolean;
  withArrow?: boolean;
  placement?: 'top' | 'bottom' | 'left' | 'right';
  className?: string;
  width?: string;
  dataTestId?: string;
};

export function TooltipComponent({
  children,
  isOpen,
  tip,
  withArrow = true,
  placement = 'top',
  className = 'default',
  width,
  dataTestId,
}: PropsWithChildren<TooltipProps>): JSX.Element {
  const ref = useRef<HTMLDivElement | null>(null);
  const { isOpened, handleOpen, handleClose } = useControl(isOpen);

  useOutsideClick(ref, handleClose);

  const isControlled = isOpen !== undefined;

  useEffect(() => {
    if (isControlled) {
      if (isOpen) {
        handleOpen();
      } else {
        handleClose();
      }
    }
  }, [isOpen, isControlled, handleOpen, handleClose]);

  const controllers = !isControlled && {
    onMouseEnter: handleOpen,
    onMouseLeave: handleClose,
    onClick: handleClose,
  };

  return (
    <TooltipWrapper
      ref={ref}
      {...controllers}
      className={`tooltip-${className}`}
      data-component-name="Tooltip/Tooltip"
    >
      {children}
      {isOpened && (
        <TooltipBody
          data-testid={dataTestId || (typeof tip === 'string' ? tip : '')}
          placement={placement}
          width={width}
          withArrow={withArrow}
        >
          {tip}
        </TooltipBody>
      )}
    </TooltipWrapper>
  );
}

export const Tooltip = memo<PropsWithChildren<TooltipProps>>(TooltipComponent);

const PLACEMENTS = {
  top: css<Pick<TooltipProps, 'withArrow'>>`
    top: 0;
    left: 50%;
    transform: translate(-50%, -99%);
    margin-top: -10px;

    ${({ withArrow }) =>
      withArrow &&
      css`
        &::after {
          border-left: 5px solid transparent;
          border-right: 5px solid transparent;
          border-top-width: 6px;
          border-top-style: solid;
          bottom: 0;
          left: 50%;
          transform: translate(-50%, 99%);
        }
      `}
  `,
  bottom: css<Pick<TooltipProps, 'withArrow'>>`
    bottom: 0;
    left: 50%;
    transform: translate(-50%, 99%);
    margin-bottom: -10px;

    ${({ withArrow }) =>
      withArrow &&
      css`
        &::after {
          border-left: 5px solid transparent;
          border-right: 5px solid transparent;
          border-bottom-width: 6px;
          border-bottom-style: solid;
          top: 0;
          left: 50%;
          transform: translate(-50%, -99%);
        }
      `}
  `,
  left: css<Pick<TooltipProps, 'withArrow'>>`
    top: 50%;
    left: 0;
    transform: translate(-100%, -50%);
    margin-left: -10px;

    ${({ withArrow }) =>
      withArrow &&
      css`
        &::after {
          border-top: 5px solid transparent;
          border-bottom: 5px solid transparent;
          border-left-width: 6px;
          border-left-style: solid;
          top: 50%;
          right: 0;
          transform: translate(99%, -50%);
        }
      `}
  `,
  right: css<Pick<TooltipProps, 'withArrow'>>`
    top: 50%;
    right: 0;
    transform: translate(100%, -50%);
    margin-right: -10px;

    ${({ withArrow }) =>
      withArrow &&
      css`
        &::after {
          border-top: 5px solid transparent;
          border-bottom: 5px solid transparent;
          border-right-width: 6px;
          border-right-style: solid;
          top: 50%;
          left: 0;
          transform: translate(-99%, -50%);
        }
      `}
  `,
};

const TooltipWrapper = styled.div`
  position: relative;
  display: flex;
`;

const TooltipBody = styled.span<
  Pick<Required<TooltipProps>, 'placement' | 'withArrow'> & { width?: string }
>`
  display: inline-block;

  position: absolute;
  text-align: center;

  padding: var(--tooltip-padding);
  max-width: var(--tooltip-max-width);
  white-space: normal;
  overflow-wrap: break-word;

  border-radius: var(--border-radius);
  transition: opacity 0.3s ease-out;

  font-size: 13px;

  z-index: var(--z-index-overlay);

  &::after {
    position: absolute;

    content: ' ';
    display: inline-block;
    width: 0;
    height: 0;
    border-color: var(--tooltip-bg-color);
  }

  background: var(--tooltip-bg-color);
  color: var(--tooltip-text-color);
  box-shadow: rgb(0 0 0 / 25%) 0 2px 4px;

  width: ${({ width }) => width || '120px'};
  ${({ placement }) => css`
    ${PLACEMENTS[placement]};
  `}
`;
