import type { ReactNode } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import {
  Animated,
  BackHandler,
  Dimensions,
  Easing,
  Platform,
  StyleSheet,
} from 'react-native';
import { useTheme } from '../../theme';
import Portal from '../Portal';

const DEFAULT_BACKDROP_OPACITY = 0.4;

const DEFAULT_ANIMATION_CONFIG = {
  easing: Easing.inOut(Easing.cubic),
  useNativeDriver: Platform.OS !== 'web',
  duration: 400,
};

export interface ModalProps {
  /**
   * Content of the modal.
   */
  children: ReactNode;
  /**
   * Visibility of the modal
   */
  visible?: boolean;
  /**
   * Callback when the modal is shown.
   */
  onShow?: () => void;
  /**
   * Callback when the user taps the hardware back button.
   */
  onRequestClose?: () => void;
  /**
   * TestID of the modal.
   */
  testID?: string;
  /**
   * Animation type of the modal content.
   */
  animationType?: 'none' | 'slide' | 'fade';
  /**
   * Whether to show the modal backdrop
   */
  transparent?: boolean;
  /**
   * Callback when the modal is dismissed. iOS only.
   */
  onDismiss?: () => void;
}

const Modal = ({
  children,
  visible = true,
  onShow,
  onRequestClose,
  testID,
  animationType = 'none',
  transparent = false,
  onDismiss,
}: ModalProps) => {
  const theme = useTheme();
  const animatedBackdropValue = useRef(new Animated.Value(0)).current;
  const animatedModalValue = useRef(new Animated.Value(0)).current;
  const [mounted, setMounted] = useState(visible);
  const previousVisible = useRef(false);

  // Animate in/out based on visible prop
  useEffect(() => {
    if (visible && !previousVisible.current) {
      // Show animation
      setMounted(true);

      if (animationType !== 'none') {
        if (!transparent) {
          Animated.timing(animatedBackdropValue, {
            toValue: 1,
            ...DEFAULT_ANIMATION_CONFIG,
          }).start();
        }

        Animated.timing(animatedModalValue, {
          toValue: 1,
          ...DEFAULT_ANIMATION_CONFIG,
        }).start(() => {
          onShow?.();
        });
      } else {
        onShow?.();
      }
    } else if (!visible && previousVisible.current) {
      // Hide animation
      if (animationType !== 'none') {
        if (!transparent) {
          Animated.timing(animatedBackdropValue, {
            toValue: 0,
            ...DEFAULT_ANIMATION_CONFIG,
          }).start();
        }

        Animated.timing(animatedModalValue, {
          toValue: 0,
          ...DEFAULT_ANIMATION_CONFIG,
        }).start(() => {
          setMounted(false);
          if (Platform.OS === 'ios') {
            onDismiss?.();
          }
        });
      } else {
        setMounted(false);
        if (Platform.OS === 'ios') {
          onDismiss?.();
        }
      }
    }

    previousVisible.current = visible;
  }, [visible, animationType, transparent, onShow, onDismiss]);

  // Back button handler
  useEffect(() => {
    if (!visible) return;

    const backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      () => {
        onRequestClose?.();
        return true;
      }
    );

    return () => backHandler.remove();
  }, [visible, onRequestClose]);

  const backdropOpacityAnimation = animatedBackdropValue.interpolate({
    inputRange: [0, 1],
    outputRange: [0, DEFAULT_BACKDROP_OPACITY],
  });

  const modalAnimation = animatedModalValue.interpolate({
    inputRange: [0, 1],
    outputRange:
      animationType === 'slide' ? [Dimensions.get('window').height, 0] : [0, 1],
  });

  if (!visible && !mounted) {
    return null;
  }

  return (
    <Portal>
      <Animated.View
        style={{
          ...StyleSheet.absoluteFillObject,
          backgroundColor: transparent
            ? 'transparent'
            : theme.colors.overlayGlobalSurface,
          opacity:
            animationType !== 'none'
              ? backdropOpacityAnimation
              : DEFAULT_BACKDROP_OPACITY,
        }}
      />

      <Animated.View
        testID={testID}
        style={{
          ...StyleSheet.absoluteFillObject,
          opacity: animationType === 'fade' ? modalAnimation : 1,
          transform: [
            {
              translateY: animationType === 'slide' ? modalAnimation : 0,
            },
          ],
        }}
      >
        {children}
      </Animated.View>
    </Portal>
  );
};

export default Modal;
