import React, { useState } from 'react'
import {
  autoPlacement,
  autoUpdate,
  flip,
  FloatingPortal,
  offset,
  type Placement,
  safePolygon,
  shift,
  useDismiss,
  useFloating,
  useFocus,
  useHover,
  useInteractions,
  useRole,
  useTransitionStyles,
} from '@floating-ui/react'

import styles from './_tooltip.module.scss'
import Button from '../Button/Button'
import Icon from '../Icons/Icon'
import { SideDrawer } from '../SideDrawer/SideDrawer'
import { toast } from '../Toast/Toast'
import { useMediaQuery } from '../../hooks/responsiveHooks'
import { c } from '../../translations/LibraryTranslationService'

export type TooltipProps = {
  /** The element that you need to hover to show the tooltip. */
  tooltipContent?:
    | ((props: { setVisible: (value: boolean) => void }) => React.JSX.Element)
    | React.ReactNode
  /** The content inside of the tooltip. */
  children: React.ReactNode
  /** The position of the tooltip. */
  position?: Placement | 'auto'
  /** Optional className for the tooltip. */
  className?: string
  /** Optional prop to restrict the max width of the tooltip. */
  maxWidth?: string
  /** Optional prop to show the `SideDrawer` when in a mobile view. */
  useSideDrawerForMobile?: boolean
  /** Optional prop to show custom header content on `SideDrawer` for mobile view. Only applicable in case of `useSideDrawerForMobile` is set to true. */
  sideDrawerHeader?: string
  /** Optional prop to show copy icon to copy the tooltip content. */
  copyToClipboard?: boolean
  /** Optional prop to set the color of the arrow. */
  /**
   * @deprecated This prop is no longer supported.
   **/
  arrowColor?: 'medium-purple' | 'red' | 'green' | 'blue' | 'white'
  /** Optional prop to remove padding */
  noPadding?: boolean
  /** Optional prop to disable the tooltip. */
  disabled?: boolean
  /** Optional prop to disable click event propagation on the tooltip. */
  enableClickThrough?: boolean
  /** Optional prop to add a test id to the Tooltip for QA testing */
  qaTestId?: string
}

const Tooltip = ({
  tooltipContent,
  children,
  position = 'right',
  className = '',
  maxWidth = '300px',
  useSideDrawerForMobile = false,
  sideDrawerHeader = '',
  copyToClipboard = false,
  noPadding = false,
  disabled = false,
  enableClickThrough = false,
  qaTestId = 'tooltip',
}: TooltipProps): React.JSX.Element => {
  const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false)
  const screenLargerThanMd = useMediaQuery({ type: 'min', breakpoint: 'md' })
  const [isTooltipOpen, setIsTooltipOpen] = useState(false)
  const isCopyToClipboardValid =
    copyToClipboard && typeof tooltipContent === 'string'

  const {
    refs: { setReference, setFloating },
    floatingStyles,
    context,
  } = useFloating({
    open: isTooltipOpen && !disabled,
    onOpenChange: setIsTooltipOpen,
    strategy: 'fixed',
    ...(position !== 'auto' ? { placement: position } : {}),
    middleware: [
      offset(8),
      ...(position === 'auto' ? [autoPlacement()] : [flip()]), // autoPlacement() is used to automatically place the tooltip in the best position and it internally uses flip()
      shift({ padding: 4 }),
    ],
    whileElementsMounted: autoUpdate,
  })

  const role = useRole(context, { role: 'tooltip' })
  const hover = useHover(context, {
    move: false,
    delay: { open: 100, close: 0 },
    handleClose:
      // Makes the Tooltip interactive
      safePolygon({
        requireIntent: false,
        blockPointerEvents: true,
        buffer: 1,
      }),
  })
  const focus = useFocus(context)
  const dismiss = useDismiss(context, {
    ancestorScroll: true, // close Tooltip on scrolling
  })

  const { isMounted, styles: transitionStyles } = useTransitionStyles(context, {
    initial: {
      opacity: 0,
      transform: 'scale(0.8)',
    },
    duration: 200, // transition duration when opening and closing the Tooltip
  })

  const { getReferenceProps, getFloatingProps } = useInteractions([
    hover,
    focus,
    dismiss,
    role,
  ])

  if (useSideDrawerForMobile && !screenLargerThanMd) {
    return (
      <>
        <Button
          as='unstyled'
          onClick={() => setMobileDrawerOpen(true)}
          qaTestId={`${qaTestId}-button`}
        >
          {children}
        </Button>
        <SideDrawer
          isOpen={mobileDrawerOpen}
          closeCallout={() => setMobileDrawerOpen(false)}
          headerContent={sideDrawerHeader}
          onlyMobile
          qaTestId={`${qaTestId}-drawer`}
        >
          {typeof tooltipContent === 'function'
            ? tooltipContent({ setVisible: setMobileDrawerOpen })
            : tooltipContent}
        </SideDrawer>
      </>
    )
  } else {
    return (
      <>
        {/* Tooltip reference element */}
        <div
          ref={setReference}
          {...getReferenceProps()}
          className={styles.children}
          qa-test-id={`${qaTestId}-reference`}
        >
          {children}
        </div>
        {/* Floating element i.e Tooltip */}
        {isMounted && tooltipContent ? (
          <FloatingPortal>
            <div
              ref={setFloating}
              style={{
                ...floatingStyles,
                ...(maxWidth ? { maxWidth: maxWidth } : {}),
                zIndex: 2 ** 31 - 1, // Max z-index (2 ** 31 - 1) to ensure the tooltip appears above all elements and stacking contexts
              }}
              {...getFloatingProps()}
              {...(className ? { className: className } : {})}
              {...(!enableClickThrough
                ? { onClick: (e) => e.stopPropagation() }
                : {})}
              qa-test-id={`${qaTestId}-floating`}
            >
              <div
                className={`${styles.tooltipContainer} ${noPadding ? styles.noPadding : ''}`}
                style={{ ...transitionStyles }}
              >
                <div
                  className={`${isCopyToClipboardValid ? styles.copyToClipboard : ''}`}
                >
                  {typeof tooltipContent === 'function'
                    ? tooltipContent({ setVisible: setIsTooltipOpen })
                    : tooltipContent}
                  {isCopyToClipboardValid ? (
                    <div className={styles.copy}>
                      <Button
                        onClick={() => {
                          navigator.clipboard.writeText(tooltipContent)
                          toast({
                            message: c('copiedToClipboard'),
                            type: 'success',
                          })
                        }}
                        as='unstyled'
                        children={
                          <Icon
                            icon='clipboard'
                            color='dark-purple'
                            iconSize='16px'
                          />
                        }
                      />
                    </div>
                  ) : null}
                </div>
              </div>
            </div>
          </FloatingPortal>
        ) : null}
      </>
    )
  }
}

export default Tooltip
