import React, {
  memo,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import {
  Animated,
  LayoutChangeEvent,
  LayoutRectangle,
  ScrollView,
  StyleProp,
  View,
  ViewStyle,
} from 'react-native';
import {NavigationState, SceneRendererProps} from 'react-native-tab-view';
import MaskedView from '@react-native-masked-view/masked-view';
import {Gradient, GradientProps} from '@components/_core';
import {TABS_TRANSPARENT_COLOR} from '@components/_HuGo/Tabs/constants';
import {ScrollEvent} from '@interfaces/generic';
import {useTheme} from '@shared/theme';

import {TabItem} from '../../../../interfaces';
import {Tab} from '../Tab';
import TabsHeaderReanimatedIndicators from './TabsHeaderReanimatedIndicators';
import {styles} from './styles';

import type {SharedValue} from 'react-native-reanimated';

interface Props extends SceneRendererProps {
  navigationState: NavigationState<TabItem>;
  withDivider?: boolean;
  fullWidth?: boolean;
  /**
   * When using react-native-collapsible-tab-view, drive tab indicators on the UI thread
   * (avoids bridging `indexDecimal` to RN `Animated` via `runOnJS` every frame).
   */
  indexDecimal?: SharedValue<number>;
  containerStyle?: StyleProp<ViewStyle>;
}

function TabHeaderComponent({
  jumpTo,
  navigationState,
  position,
  withDivider,
  fullWidth,
  indexDecimal,
  containerStyle,
}: Props) {
  const scrollViewRef = useRef<ScrollView>(null);
  const {theme} = useTheme();
  const [tabDimensions, setTabDimensions] = useState<
    {width: number; x: number; index: number}[]
  >([]);
  const [scrollInfo, setScrollInfo] = useState({
    scrollX: 0,
    contentWidth: 0,
    containerWidth: 0,
  });
  const previousIndexRef = useRef<number>(navigationState.index);
  const totalTabs = navigationState.routes.length;
  const withFullWidth = fullWidth && totalTabs <= 3;
  const {inputRange, translateXOutputRange, scaleXOutputRange, baseWidth} =
    useMemo(() => {
      // Use the smallest tab width as base width to ensure proper scaling
      const minTabWidth =
        tabDimensions.length > 0
          ? Math.min(...tabDimensions.map(d => d.width))
          : 20;

      return {
        inputRange: tabDimensions.map((_, i) => i),
        translateXOutputRange: tabDimensions.map(
          e => e.x + e.width / 2 - minTabWidth / 2,
        ),
        // Calculate proper scale factors based on the ratio to base width
        scaleXOutputRange: tabDimensions.map(e => e.width / minTabWidth),
        baseWidth: minTabWidth,
      };
    }, [tabDimensions]);
  const activeKey = navigationState.index;
  const enableAnimation = tabDimensions.length === totalTabs;
  const activeTab = tabDimensions[activeKey];

  // Calculate scroll state
  const scrollState = useMemo(() => {
    const {scrollX, contentWidth, containerWidth} = scrollInfo;
    const hasScrollContent = contentWidth > containerWidth;
    const isAtStart = scrollX <= 5; // Small threshold for scroll position
    const isAtEnd = scrollX >= contentWidth - containerWidth - 5;

    return {
      hasScrollContent,
      canScrollLeft: hasScrollContent && !isAtStart,
      canScrollRight: hasScrollContent && !isAtEnd,
    };
  }, [scrollInfo]);

  // Dynamic mask gradient based on scroll state - only right side fade
  const maskGradient: {
    colors: GradientProps['colors'];
    locations: GradientProps['locations'];
  } = useMemo(() => {
    const {canScrollRight} = scrollState;

    if (canScrollRight && !withFullWidth) {
      return {
        colors: [
          theme.background.layout.tertiary,
          theme.background.layout.tertiary,
          theme.background.layout.tertiary,
          TABS_TRANSPARENT_COLOR,
        ],
        locations: [0, 0.8, 0.9, 1],
      };
    }

    // When no fade is needed, use a single color or two colors with valid locations
    return {
      colors: [
        theme.background.layout.tertiary,
        theme.background.layout.tertiary,
      ],
      locations: [0, 1],
    };
  }, [scrollState, theme.background.layout.tertiary, withFullWidth]);

  const onTabLayout = useCallback((layout: LayoutRectangle, index: number) => {
    setTabDimensions(current => {
      const newArray = [...current];
      newArray[index] = {width: layout.width, x: layout.x, index};
      return newArray;
    });
  }, []);

  const onTabPress = useCallback(
    (key: string, index: number) => () => {
      if (tabDimensions[index]?.x === undefined) {
        return;
      }

      scrollViewRef.current?.scrollTo({x: tabDimensions[index].x});
      jumpTo(key);
    },
    [jumpTo, tabDimensions],
  );

  const onScroll = useCallback((event: ScrollEvent) => {
    const {contentOffset} = event.nativeEvent;
    setScrollInfo(current => ({
      ...current,
      scrollX: contentOffset.x,
    }));
  }, []);

  const onContentSizeChange = useCallback((contentWidth: number) => {
    setScrollInfo(current => ({
      ...current,
      contentWidth,
    }));
  }, []);

  const onLayout = useCallback((event: LayoutChangeEvent) => {
    const {width} = event.nativeEvent.layout;
    setScrollInfo(current => ({
      ...current,
      containerWidth: width,
    }));
  }, []);

  const indicatorStyle = useMemo(() => {
    if (indexDecimal) {
      return undefined;
    }

    if (!enableAnimation) {
      return undefined;
    }

    if (tabDimensions.length === 1) {
      return {
        width: baseWidth,
        transform: [
          {translateX: translateXOutputRange[0]},
          {scaleX: scaleXOutputRange[0]},
        ],
      };
    }

    // Safety check: if we don't have valid ranges, use fallback
    if (
      inputRange.length === 0 ||
      translateXOutputRange.length === 0 ||
      inputRange.length !== translateXOutputRange.length
    ) {
      // Use the current active tab position as fallback
      const currentTab = tabDimensions[activeKey];
      if (currentTab) {
        return {
          width: baseWidth,
          transform: [
            {translateX: currentTab.x + currentTab.width / 2 - baseWidth / 2},
            {scaleX: currentTab.width / baseWidth},
          ],
        };
      }
      return undefined;
    }

    // Create a safe interpolation with additional bounds checking
    const createSafeInterpolation = (
      inputValues: number[],
      outputValues: number[],
    ) => {
      // Ensure both arrays have the same length and match totalTabs
      if (
        inputValues.length !== outputValues.length ||
        inputValues.length !== totalTabs
      ) {
        // Use direct position for current tab
        if (activeKey < outputValues.length) {
          return outputValues[activeKey];
        }
        return outputValues[0] || 0;
      }

      return position.interpolate({
        inputRange: inputValues,
        outputRange: outputValues,
        extrapolate: 'clamp',
      });
    };

    const safeTranslateX = createSafeInterpolation(
      inputRange,
      translateXOutputRange,
    );
    const safeScaleX = createSafeInterpolation(inputRange, scaleXOutputRange);

    return {
      width: baseWidth,
      transform: [{translateX: safeTranslateX}, {scaleX: safeScaleX}],
    };
  }, [
    enableAnimation,
    inputRange,
    position,
    scaleXOutputRange,
    tabDimensions,
    translateXOutputRange,
    baseWidth,
    activeKey,
    totalTabs,
    indexDecimal,
  ]);

  const tabStyle = useMemo(() => {
    const width = scrollInfo.containerWidth / totalTabs - 4;
    return withFullWidth ? {width} : undefined;
  }, [withFullWidth, totalTabs, scrollInfo.containerWidth]);

  const ScrollContent = useMemo(() => {
    return (
      <ScrollView
        ref={scrollViewRef}
        bounces={false}
        showsHorizontalScrollIndicator={false}
        horizontal
        contentContainerStyle={styles.content}
        onScroll={onScroll}
        onContentSizeChange={onContentSizeChange}
        onLayout={onLayout}
        scrollEventThrottle={16}>
        {navigationState.routes.map((route, index) => (
          <Tab
            key={route.key}
            label={route.label}
            badge={route.badge}
            onPress={onTabPress(route.key, index)}
            onLayout={layout => onTabLayout(layout, index)}
            selected={index === activeKey}
            style={tabStyle}
          />
        ))}
        {indexDecimal ? (
          <TabsHeaderReanimatedIndicators
            indexDecimal={indexDecimal}
            inputRange={inputRange}
            translateXOutputRange={translateXOutputRange}
            scaleXOutputRange={scaleXOutputRange}
            baseWidth={baseWidth}
            enableAnimation={enableAnimation}
            totalTabs={totalTabs}
            tabDimensionsLength={tabDimensions.length}
            pillBackgroundColor={theme.action.background.neutral.hover}
            lineColor={theme.button.background.primary.default}
          />
        ) : (
          <>
            <Animated.View
              style={[
                styles.selectedTabIndicator,
                {backgroundColor: theme.action.background.neutral.hover},
                indicatorStyle,
              ]}
            />
            <Animated.View
              style={[
                styles.lineIndicator,
                {backgroundColor: theme.button.background.primary.default},
                indicatorStyle,
              ]}
            />
          </>
        )}
      </ScrollView>
    );
  }, [
    onScroll,
    onContentSizeChange,
    onLayout,
    navigationState.routes,
    theme.action.background.neutral.hover,
    theme.button.background.primary.default,
    indicatorStyle,
    onTabPress,
    activeKey,
    tabStyle,
    onTabLayout,
    indexDecimal,
    inputRange,
    translateXOutputRange,
    scaleXOutputRange,
    baseWidth,
    enableAnimation,
    totalTabs,
    tabDimensions.length,
  ]);

  /**
   * This is a workaround to fix the issue with the dynamic tabs.
   * Sometimes the animated indicator doesn't show correctly.
   */
  useEffect(() => {
    setTabDimensions(current => current.slice(0, totalTabs));
  }, [totalTabs]);

  /**
   * Adjust the horizontal scroll position to the active tab is not fully visible.
   */
  useEffect(() => {
    if (
      !activeTab ||
      activeTab.x === undefined ||
      previousIndexRef.current === activeTab.index
    ) {
      return;
    }

    previousIndexRef.current = activeTab.index;

    const {scrollX, containerWidth} = scrollInfo;
    const tabStart = activeTab.x;
    const tabEnd = activeTab.x + activeTab.width;
    const viewportEnd = scrollX + containerWidth;
    const isFullyVisible = tabStart >= scrollX && tabEnd <= viewportEnd;

    if (!isFullyVisible) {
      scrollViewRef.current?.scrollTo({
        x: activeTab.x,
        animated: true,
      });
    }
  }, [activeTab, scrollInfo]);

  return (
    <View
      style={[
        styles.container,
        withDivider && {
          ...styles.divider,
          borderColor: theme.border.neutral.default,
        },
        containerStyle,
      ]}>
      <MaskedView
        style={styles.maskedContainer}
        maskElement={
          <Gradient
            colors={maskGradient.colors}
            locations={maskGradient.locations}
            start={{x: 0, y: 0}}
            end={{x: 1, y: 0}}
            style={styles.maskGradient}
          />
        }>
        {ScrollContent}
      </MaskedView>
    </View>
  );
}

export const TabsHeader = memo(TabHeaderComponent);
