"use strict";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Dimensions, Modal, StyleSheet, TouchableWithoutFeedback, View } from "react-native";
import Animated, { Easing, FadeIn, FadeOut } from "react-native-reanimated";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useIOTheme, useIOThemeContext } from "../../context";
import { IOColors } from "../../core/IOColors";
import { IconButton } from "../buttons";
import { Body, H6 } from "../typography";
import { BottomArrow, LeftArrow, RightArrow, TopArrow } from "./Arrows";
import { ARROW_HEIGHT, EMPTY_SPACE, getArrowBoxByPlacement, getArrowCoords, getArrowVerticalAlignment, getDisplayInsets, getTooltipCoords, getTooltipVerticalAlignment, isDefined, isNotZero } from "./utils";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
const screenDimensions = Dimensions.get("window");
const INITIAL_COORDS = {
  x: 0,
  y: 0,
  width: 0,
  height: 0
};
const ARROWS_BY_PLACEMENT = {
  top: TopArrow,
  bottom: BottomArrow,
  left: LeftArrow,
  right: RightArrow
};
const styles = StyleSheet.create({
  backdrop: {
    position: "absolute",
    width: "100%",
    height: "100%",
    backgroundColor: IOColors.black,
    zIndex: 997
  },
  childrenContainer: {
    position: "absolute",
    zIndex: 1000
  },
  tooltipContainer: {
    position: "absolute",
    paddingHorizontal: 16,
    paddingVertical: 16,
    borderRadius: 8,
    zIndex: 2000,
    overflow: "visible"
  },
  arrowContainer: {
    position: "absolute",
    display: "flex",
    zIndex: 3000
  },
  closeIcon: {
    position: "absolute",
    right: 8,
    top: 9 // It's been used `9` instead of `8` to fix accessibility focus order. In this way title is read before close icon.
  }
});
const getChildrenPosition = childrenCoords => ({
  top: childrenCoords.y,
  left: childrenCoords.x,
  width: childrenCoords.width,
  height: childrenCoords.height
});

/**
 * Tooltip component that displays a contextual tooltip around its children.
 * The tooltip position is controlled by the `placement` prop and can adjust
 * dynamically if there is insufficient space.
 * @param {Props} props - The component props
 *
 * @returns {ReactElement} A tooltip component rendered around the specified children.
 */
export const Tooltip = ({
  children,
  title,
  content,
  placement: initialPlacement = "top",
  closeIconAccessibilityLabel,
  isVisible,
  displayInsets = {},
  allowCloseOnBackgroundTap,
  childrenInteractionsEnabled = false,
  onClose
}) => {
  const insets = useSafeAreaInsets();
  const [currentPlacement, setCurrentPlacement] = useState(initialPlacement);
  const [childrenCoords, setChildrenCoords] = useState(INITIAL_COORDS);
  const [tooltipLayout, setTooltipLayout] = useState();
  const childRef = useRef(null);
  const titleRef = useRef(null);
  const timeoutRef = useRef(undefined);

  // Theme
  const theme = useIOTheme();
  const {
    themeType
  } = useIOThemeContext();
  const backdropOpacity = themeType === "light" ? 0.4 : 0.8;
  const tooltipBackground = IOColors[theme["appBackground-secondary"]];
  const Arrow = useMemo(() => ARROWS_BY_PLACEMENT[currentPlacement], [currentPlacement]);
  const childrenCoordsValues = Object.values(childrenCoords);
  const isChildrenMeasurementFinished = childrenCoordsValues.every(isDefined) && childrenCoordsValues.some(isNotZero);
  const isTooltipMeasurementCompleted = isDefined(tooltipLayout);
  const tooltipVisibility = {
    opacity: isTooltipMeasurementCompleted ? 1 : 0
  };

  /**
   * This function sets the `Tooltip` children coordinates
   */
  const measureChildrenCoords = useCallback(() => {
    if (childRef.current && typeof childRef.current.measure === "function") {
      childRef.current.measure((_, __, width, height, px, py) => {
        const coords = {
          x: px,
          y: py,
          width,
          height
        };
        if (Object.values(coords).every(isDefined)) {
          setChildrenCoords(coords);
        }
      });
    }
  }, []);
  useEffect(() => {
    if (isVisible) {
      // A new measure is executed every time the `Tooltip` is visible
      // This is required for use within ScrollView components.
      // eslint-disable-next-line functional/immutable-data
      timeoutRef.current = setTimeout(measureChildrenCoords, 100);
    } else {
      setChildrenCoords(INITIAL_COORDS);
      setCurrentPlacement(initialPlacement);
    }
    return () => {
      if (isVisible) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, [isVisible, initialPlacement, measureChildrenCoords]);

  /**
   * This function works with `top` and `bottom` placement and sets the current placement to their opposite value
   * if in the selected one there is no space to prompt the tooltip
   */
  const invertPlacementIfNeeded = useCallback(nativeEvent => {
    if (initialPlacement === "top") {
      const hasSpace = nativeEvent.layout.y >= insets.top;
      if (!hasSpace) {
        setCurrentPlacement("bottom");
      }
    }
    if (initialPlacement === "bottom") {
      const remainingSpace = screenDimensions.height - nativeEvent.layout.y - insets.bottom;
      const tooltipMinHeight = nativeEvent.layout.height + ARROW_HEIGHT + EMPTY_SPACE;
      const hasSpace = remainingSpace >= tooltipMinHeight;
      if (!hasSpace) {
        setCurrentPlacement("top");
      }
    }
  }, [insets.bottom, insets.top, initialPlacement]);
  const handleTooltipOnLayout = useCallback(({
    nativeEvent
  }) => {
    invertPlacementIfNeeded(nativeEvent);
    setTooltipLayout(nativeEvent.layout);
  }, [invertPlacementIfNeeded]);
  const handleTapOnBackground = useCallback(() => {
    if (allowCloseOnBackgroundTap) {
      onClose();
    }
  }, [allowCloseOnBackgroundTap, onClose]);
  return /*#__PURE__*/_jsxs(_Fragment, {
    children: [/*#__PURE__*/_jsx(View
    // This prop is necessary for .measure to work correctly, as explained here: https://github.com/facebook/react-native/issues/29712
    , {
      collapsable: false,
      ref: childRef,
      children: children
    }), /*#__PURE__*/_jsxs(Modal, {
      transparent: true,
      visible: isVisible && isChildrenMeasurementFinished,
      children: [/*#__PURE__*/_jsx(View, {
        accessibilityElementsHidden: !childrenInteractionsEnabled,
        importantForAccessibility: childrenInteractionsEnabled ? "auto" : "no-hide-descendants",
        pointerEvents: childrenInteractionsEnabled ? "auto" : "box-only",
        style: [styles.childrenContainer, getChildrenPosition(childrenCoords)],
        children: children
      }), /*#__PURE__*/_jsx(TouchableWithoutFeedback, {
        accessible: allowCloseOnBackgroundTap,
        accessibilityRole: allowCloseOnBackgroundTap ? "button" : "none",
        importantForAccessibility: allowCloseOnBackgroundTap ? "yes" : "no",
        accessibilityElementsHidden: !allowCloseOnBackgroundTap,
        onPress: handleTapOnBackground,
        children: /*#__PURE__*/_jsx(Animated.View, {
          entering: FadeIn.duration(200).easing(Easing.inOut(Easing.quad))
          // The exiting transition is not visible, due to the component being unmounted
          // before the transition ends. I leave it here for future reference.
          ,
          exiting: FadeOut.duration(200).easing(Easing.inOut(Easing.quad)),
          children: /*#__PURE__*/_jsx(View, {
            style: [styles.backdrop, {
              opacity: backdropOpacity,
              height: screenDimensions.height
            }]
          })
        })
      }), /*#__PURE__*/_jsxs(View, {
        onLayout: handleTooltipOnLayout,
        style: [styles.tooltipContainer, {
          backgroundColor: tooltipBackground
        }, getTooltipCoords(currentPlacement, childrenCoords, getDisplayInsets(displayInsets), screenDimensions), getTooltipVerticalAlignment(currentPlacement, childrenCoords.height, tooltipLayout?.height), tooltipVisibility],
        children: [/*#__PURE__*/_jsx(H6, {
          ref: titleRef,
          children: title
        }), /*#__PURE__*/_jsx(View, {
          style: styles.closeIcon,
          children: /*#__PURE__*/_jsx(IconButton, {
            color: "neutral",
            icon: "closeSmall",
            accessibilityLabel: closeIconAccessibilityLabel,
            onPress: onClose
          })
        }), /*#__PURE__*/_jsx(Body, {
          children: content
        })]
      }), /*#__PURE__*/_jsx(View, {
        style: [styles.arrowContainer, getArrowBoxByPlacement(currentPlacement), getArrowCoords(currentPlacement, childrenCoords, screenDimensions), getArrowVerticalAlignment(currentPlacement, childrenCoords.height), tooltipVisibility],
        children: /*#__PURE__*/_jsx(Arrow, {
          color: tooltipBackground
        })
      })]
    })]
  });
};
//# sourceMappingURL=Tooltip.js.map