import React, { forwardRef } from 'react';
import type { StyleProp, TextStyle, ViewStyle } from 'react-native';
import {
  Animated,
  LayoutAnimation,
  Platform,
  StyleSheet,
  UIManager,
} from 'react-native';
import { useTheme } from '../../theme';
import type { IconName } from '../Icon';
import { AnimatedFABIcon } from './AnimatedFABIcon';
import {
  StyledFAB,
  StyledFABIcon,
  StyledFABText,
  StyledIconContainer,
} from './StyledFAB';
import { noop } from '../../utils/functions';

export type FABHandles = {
  show: (animated?: boolean, callback?: Animated.EndCallback) => void;
  collapse: (animated?: boolean, callback?: Animated.EndCallback) => void;
  hide: (animated?: boolean, callback?: Animated.EndCallback) => void;
};

if (Platform.OS === 'android') {
  if (UIManager.setLayoutAnimationEnabledExperimental) {
    UIManager.setLayoutAnimationEnabledExperimental(true);
  }
}

export interface FABProps {
  /**
   * Name of the Icon.
   */
  icon: IconName;

  /**
   * title of the component.
   */
  title?: string;

  /**
   * This function is called on pressing the button.
   */
  onPress?: () => void;

  /**
   * Specify if the button is animated.
   */
  animated?: boolean;

  /**
   * Specify if the button is in active state. It only works if animated is true.
   */
  active?: boolean;

  /**
   * Additional style.
   */
  style?: StyleProp<ViewStyle>;

  /**
   * Testing id of the component.
   */
  testID?: string;
}

// Extends FABProps with internal-only props. Not part of the public API.
type InternalFABProps = FABProps & {
  titleStyle?: StyleProp<TextStyle>;
};

const IconOnlyContent = ({
  icon,
  animated,
  active,
}: {
  icon: IconName;
  animated?: boolean;
  active?: boolean;
}) => {
  if (animated) {
    return (
      <StyledIconContainer>
        <AnimatedFABIcon
          active={active}
          icon={icon}
          testID="animated-fab-icon"
          size="xsmall"
        />
      </StyledIconContainer>
    );
  }
  return (
    <StyledIconContainer>
      <StyledFABIcon size="xsmall" icon={icon} testID="styled-fab-icon" />
    </StyledIconContainer>
  );
};

const IconWithTextContent = ({
  icon,
  title,
  titleStyle,
}: {
  icon: IconName;
  title?: string;
  titleStyle?: StyleProp<TextStyle>;
}) => (
  <>
    <StyledIconContainer>
      <StyledFABIcon size="xsmall" icon={icon} testID="styled-fab-icon" />
    </StyledIconContainer>
    <StyledFABText style={titleStyle}>{title}</StyledFABText>
  </>
);

const animateWidth = () => {
  LayoutAnimation.configureNext({
    duration: Platform.OS === 'ios' ? 200 : 400,
    update: {
      type: 'spring',
      springDamping: Platform.OS === 'ios' ? 1 : 1.5,
    },
  });
};

// Full implementation — accepts InternalFABProps including titleStyle.
// Exported as FABInternal for use by sibling HD components (e.g. FAB.Pair).
const FABWithTitleStyle = forwardRef<FABHandles, InternalFABProps>(
  (
    {
      onPress,
      title,
      icon,
      animated: iconAnimated,
      testID,
      active,
      style,
      titleStyle,
    },
    ref
  ) => {
    const theme = useTheme();
    const [displayState, setDisplayState] = React.useState({
      hideTitle: false,
      hideButton: false,
    });
    const isIconOnly = displayState.hideTitle || active || !title;

    const animatedValues = {
      opacity: React.useRef(new Animated.Value(1)).current,
      width: React.useRef(new Animated.Value(1)).current,
      translateY: React.useRef(new Animated.Value(0)).current,
    };

    const marginBottom = Number(StyleSheet.flatten(style)?.marginBottom) || 0;
    const [buttonWidth, setButtonWidth] = React.useState(0);
    const hasSetButtonWidth = buttonWidth > 0;

    React.useImperativeHandle(
      ref,
      () => ({
        show: (animated = true, callback: Animated.EndCallback = noop) => {
          setDisplayState({
            hideButton: false,
            hideTitle: false,
          });

          animateWidth();

          if (animated) {
            Animated.parallel([
              Animated.spring(animatedValues.opacity, {
                toValue: 1,
                useNativeDriver: true,
              }),
              Animated.spring(animatedValues.translateY, {
                toValue: 0,
                useNativeDriver: true,
              }),
            ]).start(callback);
          } else {
            animatedValues.opacity.setValue(1);
            animatedValues.translateY.setValue(0);
          }
        },
        collapse: (animated = true, callback: Animated.EndCallback = noop) => {
          setDisplayState({
            hideButton: false,
            hideTitle: true,
          });

          animateWidth();

          if (animated) {
            Animated.parallel([
              Animated.spring(animatedValues.opacity, {
                toValue: 1,
                useNativeDriver: true,
              }),
              Animated.spring(animatedValues.translateY, {
                toValue: 0,
                useNativeDriver: true,
              }),
            ]).start(callback);
          } else {
            animatedValues.opacity.setValue(1);
            animatedValues.translateY.setValue(0);
          }
        },
        hide: (animated = true, callback: Animated.EndCallback = noop) => {
          if (animated) {
            Animated.stagger(20, [
              Animated.spring(animatedValues.opacity, {
                toValue: 0,
                useNativeDriver: true,
              }),
              Animated.spring(animatedValues.translateY, {
                toValue: 1,
                useNativeDriver: true,
              }),
            ]).start((arg) => {
              animateWidth();
              setDisplayState((previousState) => ({
                ...previousState,
                hideButton: true,
              }));
              if (callback) {
                callback(arg);
              }
            });
          } else {
            animatedValues.opacity.setValue(0);
            animatedValues.translateY.setValue(1);
            animateWidth();
            setDisplayState((previousState) => ({
              ...previousState,
              hideButton: true,
            }));
          }
        },
      }),
      []
    );

    return (
      <StyledFAB
        onLayout={(event) =>
          !hasSetButtonWidth &&
          !active &&
          setButtonWidth(event.nativeEvent.layout.width)
        }
        activeOpacity={0.8}
        onPress={onPress}
        themeIconOnly={isIconOnly}
        style={[
          style,
          {
            bottom: displayState.hideButton
              ? -(marginBottom + theme.__hd__.fab.sizes.height * 2)
              : StyleSheet.flatten(style)?.bottom,

            transform: [
              {
                translateY: animatedValues.translateY.interpolate({
                  inputRange: [0, 1],
                  outputRange: [
                    0,
                    marginBottom + theme.__hd__.fab.sizes.height * 2,
                  ],
                }),
              },
            ],
          },
        ]}
        testID={testID}
        themeActive={active}
      >
        <Animated.View
          style={{
            flexDirection: 'row',
            opacity: animatedValues.opacity.interpolate({
              inputRange: [0, 1],
              outputRange: [0, 1],
            }),
          }}
        >
          {isIconOnly ? (
            <IconOnlyContent
              animated={iconAnimated}
              active={active}
              icon={active ? 'add' : icon}
            />
          ) : (
            <IconWithTextContent
              icon={icon}
              title={title}
              titleStyle={titleStyle}
            />
          )}
        </Animated.View>
      </StyledFAB>
    );
  }
);

FABWithTitleStyle.displayName = 'FAB';

// Public export — clean FABProps, no internal props exposed.
// Docgen reads this component and sees only FABProps.
const FAB = forwardRef<FABHandles, FABProps>((props, ref) => (
  <FABWithTitleStyle {...props} ref={ref} />
));

FAB.displayName = 'FAB';

export { FABWithTitleStyle as FABInternal };
export default FAB;
