import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react';

import { Pressable, View } from 'react-native';
import Animated, { Easing, useSharedValue, withTiming } from 'react-native-reanimated';
import { useNativeMergeStyles, useStyles } from '@cleartrip/ct-design-style-manager';
import { Container, ContainerRef } from '@cleartrip/ct-design-container';
import { Typography } from '@cleartrip/ct-design-typography';
import Conditional from '@cleartrip/ct-design-conditional-wrap';
import { TooltipChevronDown } from '@cleartrip/ct-design-icons';

import { getTooltipArrowStyles, getTooltipOverlayStyles, staticTooltipStyles } from './style';
import { IDimensionsProps, IMeasurements, ITooltipProps } from './type';
import { Theme } from '@cleartrip/ct-design-theme';

export const getDefaultTooltipStyles = (theme: Theme) => {
  return {
    backgroundColor: theme.color.tooltip.primaryBg,
    borderRadius: theme.border.radius[4],
  };
};

const initialDimensions: IDimensionsProps = {
  width: 0,
  height: 0,
  x: 0,
  y: 0,
  leftEdge: 0,
  rightEdge: 0,
};

const measure = (ref: React.RefObject<View | null>): Promise<IDimensionsProps> => {
  return new Promise((resolve) => {
    ref.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
      if (width !== undefined && height !== undefined && pageX !== undefined && pageY !== undefined) {
        resolve({
          width: Math.floor(width),
          height: Math.floor(height),
          x: Math.floor(x),
          y: Math.floor(y),
          leftEdge: pageX,
          rightEdge: pageX + Math.floor(width),
        });
      } else {
        resolve(initialDimensions);
      }
    });
  });
};

const Tooltip = forwardRef<ContainerRef, ITooltipProps>(
  (
    {
      children,
      position = 'right',
      labelContainer,
      tooltipWidth = 150,
      tooltipBorderRadius = 'md',
      styleConfig,
      open = false,
      animate = true,
      svgPathProps,
      anchorRef,
    },
    ref,
  ) => {
    const {
      root: customRootStyles = [],
      labelContainerStyles: customLabelContainerStyles = [],
      overlayStyles: customOverlayStyles = [],
      arrowStyles: customArrowStyles = [],
    } = styleConfig || {};
    const [isHovered, setIsHovered] = useState<boolean>(open);
    const labelContainerRef = useRef<View>(null);
    const overlayRef = useRef<View>(null);
    const fadeAnim = useSharedValue(0);
    const [measurements, setMeasurements] = useState<IMeasurements>({
      labelContainer: initialDimensions,
      overlayContainer: initialDimensions,
      anchorRef: initialDimensions,
    });

    const dynamicTooltipStyles = useStyles(
      (theme) => {
        return {
          overlayStyles: {
            opacity: isHovered ? 1 : 0,
            width: tooltipWidth,
            ...getTooltipOverlayStyles({
              theme,
              borderRadius: tooltipBorderRadius,
              overlayWidth: tooltipWidth,
              position,
              measurements,
            }),
          },
        };
      },
      [position, measurements, tooltipWidth, isHovered, tooltipBorderRadius],
    );

    const dynamicTooltipArrowStyles = useStyles(() => {
      return {
        tooltipArrow: {
          opacity: isHovered ? 1 : 0,
          ...getTooltipArrowStyles({
            position,
            measurements,
          }),
        },
      };
    }, [isHovered, position, measurements]);

    const mergedLabelContainerStyles = useNativeMergeStyles(
      [staticTooltipStyles?.labelContainerStyles, ...customLabelContainerStyles],
      [customLabelContainerStyles],
    );

    const handleLayout = useCallback(() => {
      const calculatePosition = async () => {
        try {
          const labelContainer = await measure(labelContainerRef);
          const overlayContainer = await measure(overlayRef);

          /**
           * force type casting
           */
          const wrapper = anchorRef ? await measure(anchorRef as React.RefObject<View>) : initialDimensions;

          setMeasurements({
            labelContainer,
            overlayContainer,
            anchorRef: wrapper,
          });
        } catch (_error) {
          // Error handling without console.log
        }
      };

      calculatePosition();
    }, [anchorRef]);

    const handlePress = useCallback((isVisible: boolean) => setIsHovered(isVisible), []);

    useEffect(() => {
      if (isHovered) {
        if (animate) {
          fadeAnim.value = withTiming(1, {
            duration: 200,
            easing: Easing.inOut(Easing.ease),
          });
        } else {
          fadeAnim.value = 1;
        }
      } else {
        if (animate) {
          fadeAnim.value = withTiming(0, {
            duration: 200,
            easing: Easing.inOut(Easing.ease),
          });
        } else {
          fadeAnim.value = 0;
        }
      }
    }, [isHovered, fadeAnim, animate]);

    useEffect(() => {
      setIsHovered(open);
    }, [open]);

    return (
      <Container
        ref={ref}
        styleConfig={{ root: [staticTooltipStyles?.root, ...customRootStyles] }}
        onLayout={handleLayout}
      >
        <Pressable
          ref={labelContainerRef}
          style={mergedLabelContainerStyles}
          onPressIn={() => handlePress(true)}
          onPressOut={() => handlePress(false)}
        >
          {labelContainer}
        </Pressable>

        {!!children && (
          <>
            <Animated.View style={{ opacity: fadeAnim, position: 'absolute' }}>
              <View ref={overlayRef}>
                <Container
                  styleConfig={{
                    root: [
                      staticTooltipStyles?.overlayStyles,
                      dynamicTooltipStyles?.overlayStyles,
                      ...customOverlayStyles,
                    ],
                  }}
                >
                  <Conditional
                    condition={typeof children === 'string'}
                    wrap={() => (
                      <Typography variant='B2' color='neutral'>
                        {children}
                      </Typography>
                    )}
                  >
                    {children}
                  </Conditional>
                </Container>

                <Container
                  styleConfig={{
                    root: [
                      staticTooltipStyles?.tooltipArrow,
                      dynamicTooltipArrowStyles?.tooltipArrow,
                      ...customArrowStyles,
                    ],
                  }}
                >
                  <TooltipChevronDown {...svgPathProps} />
                </Container>
              </View>
            </Animated.View>
          </>
        )}
      </Container>
    );
  },
);
Tooltip.displayName = 'Tooltip';
export default Tooltip;
