import _extends from "@babel/runtime/helpers/extends";
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { fg } from '@atlaskit/platform-feature-flags';
import Popup from '@atlaskit/popup';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { ActionName, CardDisplay } from '../../../constants';
import { useSmartCardActions } from '../../../state/actions';
import { useSmartLinkRenderers } from '../../../state/renderers';
import { useSmartCardState as useLinkState } from '../../../state/store';
import { SmartLinkAnalyticsContext } from '../../../utils/analytics/SmartLinkAnalyticsContext';
import CustomPopupContainer, { createCustomPopupContainer } from '../components/CustomPopupContainer';
import HoverCardContent from '../components/HoverCardContent';
import { CARD_GAP_PX, HOVER_CARD_Z_INDEX } from '../styled';
export const HOVER_CARD_SOURCE = 'smartLinkPreviewHoverCard';
const HOVER_CARD_TRIGGER_WRAPPER = 'hover-card-trigger-wrapper';
const FADE_IN_DELAY = 500;
const FADE_OUT_DELAY = 300;
const RESOLVE_DELAY = 100;
export const HoverCardComponent = ({
  children,
  url,
  id = '',
  canOpen = true,
  closeOnChildClick = false,
  actionOptions,
  allowEventPropagation = false,
  zIndex = HOVER_CARD_Z_INDEX,
  noFadeDelay = false,
  hoverPreviewOptions,
  role,
  shouldRenderToParent = false,
  label,
  titleId,
  onVisibilityChange
}) => {
  var _hoverPreviewOptions$;
  const fadeInDelay = (_hoverPreviewOptions$ = hoverPreviewOptions === null || hoverPreviewOptions === void 0 ? void 0 : hoverPreviewOptions.fadeInDelay) !== null && _hoverPreviewOptions$ !== void 0 ? _hoverPreviewOptions$ : FADE_IN_DELAY;
  const [isOpen, setIsOpen] = React.useState(false);
  const fadeOutTimeoutId = useRef();
  const fadeInTimeoutId = useRef();
  const resolveTimeOutId = useRef();
  const mousePos = useRef();
  const popupOffset = useRef();
  const parentSpan = useRef(null);
  const renderers = useSmartLinkRenderers();
  const linkState = useLinkState(url);
  const handleSetIsOpen = fg('hover-card-on-visibility-change-callback') ?
  // eslint-disable-next-line react-hooks/rules-of-hooks
  useCallback(isOpen => {
    setIsOpen(isOpen);
    if (fg('hover-card-on-visibility-change-callback')) {
      onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(isOpen);
    }
  }, [onVisibilityChange]) : undefined;
  const {
    loadMetadata
  } = useSmartCardActions(id, url);
  const setMousePosition = useCallback(event => {
    if (isOpen && canOpen) {
      return;
    }
    mousePos.current = {
      x: event.clientX,
      y: event.clientY
    };

    //If these are undefined then popupOffset is undefined and we fallback to default bottom-start placement
    if ((!isOpen || !canOpen) && parentSpan.current && mousePos.current) {
      const {
        bottom,
        left
      } = parentSpan.current.getBoundingClientRect();
      popupOffset.current = [mousePos.current.x - left + CARD_GAP_PX, mousePos.current.y - bottom + CARD_GAP_PX];
    }
  }, [canOpen, isOpen]);
  const hideCard = useCallback(() => {
    if (handleSetIsOpen && fg('hover-card-on-visibility-change-callback')) {
      handleSetIsOpen(false);
    } else {
      setIsOpen(false);
    }
  }, [handleSetIsOpen]);
  const initHideCard = useCallback(() => {
    if (fadeInTimeoutId.current) {
      clearTimeout(fadeInTimeoutId.current);
      // because fadeInTimeoutId.current is set by mouseOver which triggers multiple times in a hover,
      // we want to clear out the reference to signify that there's no in-progress fade in event
      fadeInTimeoutId.current = undefined;
    }
    if (resolveTimeOutId.current) {
      clearTimeout(resolveTimeOutId.current);
      // because resolveTimeOutId.current is set by mouseOver which triggers multiple times in a hover,
      // we want to clear out the reference to signify that there's no in-progress resolve event
      resolveTimeOutId.current = undefined;
    }
    if (noFadeDelay) {
      hideCard();
    } else {
      fadeOutTimeoutId.current = setTimeout(() => hideCard(), FADE_OUT_DELAY);
    }
  }, [hideCard, noFadeDelay]);

  // clearing out the timeouts in order to avoid memory leaks
  // in case the component unmounts before they execute
  useEffect(() => {
    return () => {
      if (fadeOutTimeoutId.current) {
        clearTimeout(fadeOutTimeoutId.current);
      }
      if (fadeInTimeoutId.current) {
        clearTimeout(fadeInTimeoutId.current);
        // because fadeInTimeoutId.current is set by mouseOver which triggers multiple times in a hover,
        // we want to clear out the reference to signify that there's no in-progress fade in event
        fadeInTimeoutId.current = undefined;
      }
      if (resolveTimeOutId.current) {
        clearTimeout(resolveTimeOutId.current);
        // because resolveTimeOutId.current is set by mouseOver which triggers multiple times in a hover,
        // we want to clear out the reference to signify that there's no in-progress resolve event
        resolveTimeOutId.current = undefined;
      }
    };
  }, []);

  // we want to initiate resolve a bit earlier for standalone cards
  // to minimize the loading state
  const initResolve = useCallback(() => {
    // this check covers both non-SSR (status) & SSR case (metadataStatus)
    const isLinkUnresolved = linkState.status === 'pending' || !linkState.metadataStatus;
    if (!resolveTimeOutId.current && isLinkUnresolved) {
      resolveTimeOutId.current = setTimeout(() => {
        loadMetadata();
      }, RESOLVE_DELAY);
    }
  }, [linkState.metadataStatus, linkState.status, loadMetadata]);
  const initShowCard = useCallback(event => {
    // clearing out fadeOutTimeoutId in case it's already counting down to hide the card
    if (fadeOutTimeoutId.current) {
      clearTimeout(fadeOutTimeoutId.current);
    }

    // starting to resolve the hover card if the store doesn't have data about the link yet
    initResolve();

    //Set mouse position in the case it's not already set by onMouseMove, as in the case of scrolling
    setMousePosition(event);
    if (!isOpen && !fadeInTimeoutId.current) {
      // setting a timeout to show a Hover Card after delay runs out
      if (noFadeDelay) {
        if (handleSetIsOpen && fg('hover-card-on-visibility-change-callback')) {
          handleSetIsOpen(true);
        } else {
          setIsOpen(true);
        }
      } else {
        fadeInTimeoutId.current = setTimeout(() => {
          if (handleSetIsOpen && fg('hover-card-on-visibility-change-callback')) {
            handleSetIsOpen(true);
          } else {
            setIsOpen(true);
          }
        }, fadeInDelay);
      }
    }
  }, [initResolve, isOpen, setMousePosition, noFadeDelay, fadeInDelay, handleSetIsOpen]);
  const onActionClick = useCallback(actionId => {
    if (actionId === ActionName.PreviewAction || actionId === ActionName.AutomationAction) {
      hideCard();
    }
  }, [hideCard]);

  // Stop hover preview content to propagate event to parent.
  const handleChildClick = useCallback((e, closeOnClick) => {
    if (!allowEventPropagation) {
      e.stopPropagation();
    }
    if (closeOnClick) {
      hideCard();
    }
  }, [allowEventPropagation, hideCard]);
  const onContextMenuClick = useCallback(e => handleChildClick(e, true), [handleChildClick]);
  const onChildClick = useCallback(e => handleChildClick(e, closeOnChildClick), [closeOnChildClick, handleChildClick]);
  const content = useCallback(({
    update
  }) => {
    const hoverCardContentProps = {
      onMouseEnter: initShowCard,
      onMouseLeave: initHideCard,
      cardState: linkState,
      onActionClick,
      onResolve: update,
      renderers,
      actionOptions,
      url,
      id,
      hoverPreviewOptions
    };
    return /*#__PURE__*/React.createElement(SmartLinkAnalyticsContext, {
      url: url,
      display: CardDisplay.HoverCardPreview,
      id: id,
      source: HOVER_CARD_SOURCE
    }, /*#__PURE__*/React.createElement(HoverCardContent, hoverCardContentProps));
  }, [initShowCard, initHideCard, linkState, onActionClick, renderers, actionOptions, url, id, hoverPreviewOptions]);
  const trigger = useCallback(({
    'aria-haspopup': ariaHasPopup,
    'aria-expanded': ariaExpanded,
    ...triggerProps
  }) => /*#__PURE__*/React.createElement("span", {
    ref: parentSpan
  }, /*#__PURE__*/React.createElement("span", _extends({}, triggerProps, {
    // eslint-disable-next-line @atlassian/a11y/mouse-events-have-key-events
    onMouseOver: initShowCard
    // eslint-disable-next-line @atlassian/a11y/mouse-events-have-key-events
    ,
    onMouseLeave: initHideCard,
    onMouseMove: setMousePosition,
    onClick: onChildClick,
    onContextMenu: onContextMenuClick,
    "data-testid": HOVER_CARD_TRIGGER_WRAPPER
  }, editorExperiment('platform_editor_preview_panel_linking_exp', true) ? {
    className: HOVER_CARD_TRIGGER_WRAPPER
  } : {}), children)), [children, initHideCard, initShowCard, onChildClick, onContextMenuClick, setMousePosition]);
  const popupComponent = fg('hover-card-prop-should-render-to-parent') ?
  // eslint-disable-next-line react-hooks/rules-of-hooks
  useMemo(() => {
    // Within the Popup component, if shouldRenderToParent, the zIndex prop is ignored
    // as it is assumed that the custom popup container has the desired styles
    return createCustomPopupContainer(shouldRenderToParent ? zIndex : undefined);
  }, [zIndex, shouldRenderToParent]) : CustomPopupContainer;
  return /*#__PURE__*/React.createElement(Popup, _extends({
    testId: "hover-card",
    isOpen: isOpen && canOpen,
    onClose: hideCard,
    placement: "bottom-start",
    offset: popupOffset.current,
    autoFocus: false,
    content: content,
    trigger: trigger,
    zIndex: zIndex,
    role: role,
    titleId: titleId,
    label: label
  }, fg('hover-card-prop-should-render-to-parent') ? {
    shouldRenderToParent
  } : {}, {
    // @ts-ignore: [PIT-1685] Fails in post-office due to backwards incompatibility issue with React 18
    popupComponent: popupComponent
  }));
};