import MaskedView from '@react-native-masked-view/masked-view';
import { LinearGradient } from 'expo-linear-gradient';
import React, { useCallback, useState } from 'react';
import type { AccessibilityProps, LayoutChangeEvent } from 'react-native';
import { View } from 'react-native';

import { useTheme } from '../../../theme';

interface GradientTextProps extends AccessibilityProps {
  children: React.ReactNode;
}

const GradientText = ({
  children,
  ...accessibilityProps
}: GradientTextProps) => {
  const theme = useTheme();
  const gradient = theme.colors.gradients.aiDiagonal;
  const [size, setSize] = useState<{ width: number; height: number } | null>(
    null
  );

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

  return (
    <View {...accessibilityProps}>
      {/*
       * MaskedView is entirely hidden from the accessibility tree.
       * It is purely visual: the mask defines the text clip shape and
       * the content shows either a gradient or a visible fallback.
       * Screen readers must not traverse into either branch here.
       */}
      <MaskedView
        style={{ alignSelf: 'stretch' }}
        accessibilityElementsHidden
        importantForAccessibility="no-hide-descendants"
        maskElement={
          <View
            onLayout={onLayout}
            style={{ backgroundColor: 'transparent' }}
            testID="gradient-text-mask"
          >
            {children}
          </View>
        }
      >
        {size ? (
          <LinearGradient
            start={gradient.start}
            end={gradient.end}
            colors={gradient.colors}
            locations={gradient.locations}
            style={{ width: size.width, height: size.height }}
          />
        ) : (
          // Render children as fallback until layout is measured, so text
          // is visible immediately rather than blank on the first frame.
          children
        )}
      </MaskedView>
      {/*
       * Visually hidden but accessible: the single Text node that
       * screen readers announce. Kept outside MaskedView so it is
       * always present and never duplicated.
       */}
      <View
        style={{ position: 'absolute', opacity: 0 }}
        importantForAccessibility="yes"
        accessibilityElementsHidden={false}
      >
        {children}
      </View>
    </View>
  );
};

export default GradientText;
