import React, { useCallback, useEffect } from 'react'
import { LayoutChangeEvent, View } from 'react-native'
import { CollapseAnimationConfig, CollapseProps } from './types'
import Animated, {
  Easing,
  useSharedValue,
  useAnimatedStyle,
  withTiming,
} from 'react-native-reanimated'

export * from './types'

export const Collapse = (props: CollapseProps) => {
  const {
    open,
    children,
    style,
    contentContainerStyle,
    animationConfig,
    ...rest
  } = {
    ...Collapse.defaultProps,
    ...props,
  }

  /** Two separate SharedValues are required: `height` is the measured natural height (written once and never animated), while `animatedHeight` is the value that actually drives the transition. Keeping them separate avoids re-triggering `onLayout` effects when the animation runs. */
  const height = useSharedValue(0)
  const animatedHeight = useSharedValue(0)

  const onLayout = useCallback((event: LayoutChangeEvent) => {
    const measuredHeight = event.nativeEvent.layout.height

    /** Guard prevents overwriting the measured height if children re-trigger layout (e.g. after a font-load or image decode). */
    if (height.value != 0) return

    if (measuredHeight) {
      height.value = measuredHeight

      if (open) {
        animatedHeight.value = withTiming(measuredHeight, animationConfig)
      }
    }
  }, [open])

  /** `maxHeight` is animated rather than `height` so the inner View can still measure its natural size via `onLayout`; animating `height` directly would freeze the measurement. The `'auto'` fallback keeps content visible before the first layout completes. */
  const animatedStyle = useAnimatedStyle(() => {
    return {
      maxHeight: height.value === 0 ? 'auto' : animatedHeight.value,
      overflow: 'hidden',
    }
  })

  useEffect(() => {
    animatedHeight.value = withTiming(open ? height.value : 0, animationConfig)
  }, [open])

  return (
    <Animated.View {...rest} style={[animatedStyle, style]}>
      <View onLayout={onLayout} style={contentContainerStyle}>
        {children}
      </View>
    </Animated.View>
  )
}

const defaultAnimationConfig: CollapseAnimationConfig = {
  duration: 300,
  easing: Easing.inOut(Easing.quad),
}

Collapse.defaultProps = {
  animationConfig: defaultAnimationConfig,
} as CollapseProps
