import React, { useCallback, useLayoutEffect, useRef } from 'react';
import type { LayoutChangeEvent, StyleProp, ViewStyle } from 'react-native';
import {
  Modal,
  View,
  TouchableWithoutFeedback,
  StyleSheet,
  useWindowDimensions,
} from 'react-native';
import type { Placement } from './StyledAppCue';
import {
  StyledContainer,
  StyledContent,
  StyledIconContainer,
} from './StyledAppCue';
import Typography from '../Typography';
import Icon from '../Icon';
import { useTheme } from '../../theme';
import { calculatePosition, calulateContentMaxWidth } from './utils';

interface AppCueProps {
  /*
   * The content of the App Cue.
   */
  content: string | React.ReactElement;
  /*
   * The target where the App Cue is relatively placed to.
   */
  target: React.ReactElement<{
    onPress?: () => void;
    children?: React.ReactNode;
  }>;

  /**
   * The Position of the App Cue.
   */
  placement?: Placement;
  /**
   * Additional style.
   */
  style?: StyleProp<ViewStyle>;
  /**
   * Testing ID of the component.
   */
  testID?: string;
}

const AppCue = ({
  target,
  content,
  placement = 'top',
  style,
  testID,
}: AppCueProps) => {
  const targetContainerRef = useRef<View>(null);

  const [visible, setVisible] = React.useState(false);

  const theme = useTheme();

  const { offset } = theme.__hd__.appCue.space;

  const [position, setPosition] = React.useState({
    pageX: 0,
    pageY: 0,
    width: 0,
    height: 0,
  });

  const [contentSize, setContentSize] = React.useState({
    width: 0,
    height: 0,
  });

  const doMeasure = useCallback((cb?: () => void) => {
    targetContainerRef.current?.measure(
      (_, __, width, height, pageX, pageY) => {
        setPosition({ pageX, pageY, width, height });
        cb?.();
      }
    );
  }, []);

  const handleOpen = () => {
    doMeasure(() => setVisible(true));
  };

  useLayoutEffect(() => {
    doMeasure();
  }, [doMeasure]);

  const enhancedTarget = React.cloneElement(
    target,
    {
      onPress: () => {
        handleOpen();
        target.props?.onPress?.();
      },
    },
    target.props.children
  );

  const measureContent = (event: LayoutChangeEvent) => {
    setContentSize({
      width: event.nativeEvent.layout.width,
      height: event.nativeEvent.layout.height,
    });
  };

  const { width: windowWidth } = useWindowDimensions();

  const pos = calculatePosition({
    position,
    contentSize,
    placement,
    offset,
    windowWidth,
  });

  const maxWidth = calulateContentMaxWidth({
    position,
    offset,
    placement,
    windowWidth,
  });

  const renderContent = () => {
    if (typeof content === 'string') {
      return <Typography.Body intent="inverted">{content}</Typography.Body>;
    }
    return content;
  };

  return (
    <>
      <TouchableWithoutFeedback onPress={handleOpen}>
        <View
          collapsable={false}
          ref={targetContainerRef}
          style={{ alignSelf: 'center' }}
        >
          {enhancedTarget}
        </View>
      </TouchableWithoutFeedback>
      <Modal
        animationType="fade"
        visible={visible}
        onDismiss={() => setVisible(false)}
        transparent
        presentationStyle="overFullScreen"
        statusBarTranslucent
      >
        <TouchableWithoutFeedback
          testID={testID && `${testID}-backdrop`}
          onPress={() => setVisible(false)}
        >
          <StyledContainer>
            <View
              style={StyleSheet.flatten([
                {
                  position: 'absolute',
                  top: pos.top,
                  left: pos.left,
                  bottom: pos.bottom,
                  right: pos.right,
                },
                style,
              ])}
              onLayout={measureContent}
              testID={testID}
            >
              <StyledContent
                style={{ maxWidth }}
                testID={testID && `${testID}-content`}
              >
                {renderContent()}
              </StyledContent>
              <StyledIconContainer
                themePlacement={placement}
                testID={testID && `${testID}-arrow`}
              >
                <Icon icon="caret-down" size="small" />
              </StyledIconContainer>
            </View>
          </StyledContainer>
        </TouchableWithoutFeedback>
      </Modal>
    </>
  );
};

export default AppCue;
