import MaskedView from '@react-native-masked-view/masked-view';
import { LinearGradient } from 'expo-linear-gradient';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { LayoutChangeEvent } from 'react-native';
import { Animated, Easing, Platform, View } from 'react-native';
import { useTheme } from '../../theme';
import Typography from '../Typography';

interface AnimatedGradientTextProps {
  children: string;
  fontSize: number;
  lineHeight: number;
}

const ANIMATION_DURATION_MS = 2000;

const AnimatedGradientText = ({
  children,
  fontSize,
  lineHeight,
}: AnimatedGradientTextProps) => {
  const theme = useTheme();
  const gradient = theme.colors.gradients.aiHorizontal;

  const [size, setSize] = useState<{ width: number; height: number } | null>(
    null
  );
  const animatedValue = useRef(new Animated.Value(0));

  const onLayout = useCallback((event: LayoutChangeEvent) => {
    const { width, height } = event.nativeEvent.layout;
    setSize((prev) => {
      if (prev?.width === width && prev?.height === height) return prev;
      return { width, height };
    });
  }, []);

  useEffect(() => {
    if (!size) return;
    animatedValue.current.setValue(0);
    const animation = Animated.loop(
      Animated.timing(animatedValue.current, {
        toValue: 1,
        duration: ANIMATION_DURATION_MS,
        easing: Easing.linear,
        useNativeDriver: Platform.OS !== 'web',
      })
    );
    animation.start();
    return () => animation.stop();
  }, [size]);

  // Slide left by one full text-width per loop cycle.
  // Starting at 0 keeps the gradient visible from the first frame.
  const translateX = size
    ? animatedValue.current.interpolate({
        inputRange: [0, 1],
        outputRange: [0, -size.width],
      })
    : animatedValue.current;

  return (
    <View>
      <MaskedView
        accessibilityElementsHidden
        importantForAccessibility="no-hide-descendants"
        maskElement={
          <View
            onLayout={onLayout}
            style={{ backgroundColor: 'transparent' }}
            testID="animated-gradient-text-mask"
          >
            <Typography.Body intent="body" style={{ fontSize, lineHeight }}>
              {children}
            </Typography.Body>
          </View>
        }
      >
        {size ? (
          <View
            style={{
              width: size.width,
              height: size.height,
              overflow: 'hidden',
            }}
          >
            {/*
             * Double the colour pattern so the gradient tiles seamlessly:
             * [A, B, A, B, A] at equal spacing means the slice at
             * translateX=0 looks identical to the slice at translateX=-width,
             * eliminating the jump when the loop restarts.
             */}
            <Animated.View
              style={{
                width: size.width * 2,
                height: size.height,
                transform: [{ translateX }],
              }}
            >
              <LinearGradient
                start={gradient.start}
                end={gradient.end}
                colors={[
                  gradient.colors[0],
                  gradient.colors[1],
                  gradient.colors[2],
                  gradient.colors[1],
                  gradient.colors[0],
                ]}
                locations={[0, 0.25, 0.5, 0.75, 1.0]}
                style={{ width: '100%', height: '100%' }}
              />
            </Animated.View>
          </View>
        ) : (
          <Typography.Body intent="body" style={{ fontSize, lineHeight }}>
            {children}
          </Typography.Body>
        )}
      </MaskedView>
      {/* Visually hidden but accessible text for screen readers */}
      <View
        style={{ position: 'absolute', opacity: 0 }}
        importantForAccessibility="yes"
        accessibilityElementsHidden={false}
      >
        <Typography.Body intent="body" style={{ fontSize, lineHeight }}>
          {children}
        </Typography.Body>
      </View>
    </View>
  );
};

export default AnimatedGradientText;
