import React from 'react'
import PropTypes from 'prop-types'
import { Animated, Pressable, StyleSheet, Text, View } from 'react-native'
import { StackView } from '../StackView/StackView'
import { Icon } from '../Icon/Icon'
import { a11yProps } from '../utils/props/a11yProps'
import { getTokensPropType } from '../utils/props/tokens'
import { selectSystemProps } from '../utils/props/selectSystemProps'
import { variantProp } from '../utils/props/variantProp'
import { viewProps } from '../utils/props/viewProps'
import { applyTextStyles } from '../ThemeProvider/utils/styles'
import { useTheme } from '../ThemeProvider/useTheme'
import { useA11yInfo } from '../A11yInfoProvider/A11yInfoProvider'

// Spring config for the floating pill morph, matching the design spec. The physics are constant
// across brands, so they live here rather than as theme tokens. Animating width/borderRadius cannot
// use the native driver, so the morph always runs with useNativeDriver: false.
const PILL_SPRING = { stiffness: 300, damping: 20, mass: 1 }

// Expands the pressable dot's touch target toward the ~44px accessibility minimum without changing
// its visual size. Horizontal slop is kept small so adjacent dots don't overlap their hit areas.
const KNOB_HIT_SLOP = { top: 12, bottom: 12, left: 6, right: 6 }

// A knob is a circle/capsule, so its fully-rounded corner radius is half its diameter.
const FULL_ROUND_DIVISOR = 2

const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])

const selectCompletedIconTokens = ({ completedIconColor, completedIconSize }) => ({
  size: completedIconSize,
  color: completedIconColor
})
const selectConnectorStyles = (
  {
    connectorColor,
    connectorHeight,
    connectorMinWidth,
    connectorCompletedHeight,
    connectorCompletedColor
  },
  isCompleted
) => ({
  backgroundColor: connectorColor,
  height: connectorHeight,
  minWidth: connectorMinWidth,
  ...(isCompleted && { height: connectorCompletedHeight, backgroundColor: connectorCompletedColor })
})
const selectCurrentInnerStyles = ({ knobCurrentInnerColor, knobCurrentInnerSize }) => ({
  backgroundColor: knobCurrentInnerColor,
  borderRadius: knobCurrentInnerSize / FULL_ROUND_DIVISOR,
  height: knobCurrentInnerSize,
  width: knobCurrentInnerSize
})
/**
 * Animated overrides for the floating pill morph. Only `width` is interpolated.
 *
 * `borderRadius` is held at the constant fully-rounded value rather than interpolated: the knob
 * height is fixed at `knobSize`, so any radius >= `knobSize / 2` renders rounded ends (a circle at
 * rest, a capsule when wide). Do NOT interpolate the radius — `Animated.spring` overshoots below 0,
 * which extrapolated the radius toward 0 and produced a square flash on the shrinking dot.
 */
const selectAnimatedKnobStyle = (
  pillProgress,
  { knobSize, knobCompletedWidth, knobCompletedRadius }
) => ({
  width: pillProgress.interpolate({
    inputRange: [0, 1],
    outputRange: [knobSize, knobCompletedWidth || knobSize]
  }),
  borderRadius: knobCompletedRadius || knobSize / FULL_ROUND_DIVISOR
})
/**
 * Builds the knob style for a single dot.
 *
 * Due to a pre-existing token swap, the active (`isCurrent`) dot reads the `knobCompleted*`
 * tokens (see CarouselStepTracker.jsx). `knobCompletedWidth`/`knobCompletedRadius` turn the active
 * dot into a pill; brands that leave them unset — and the standalone StepTracker — fall back to the
 * circular `knobSize` / `knobSize / 2` geometry, so the change is backwards compatible.
 */
const selectKnobStyles = (
  {
    knobBackgroundColor,
    knobBorderColor,
    knobBorderWidth,
    knobCompletedBackgroundColor,
    knobCompletedBorderColor,
    knobCompletedPaddingLeft,
    knobCompletedPaddingTop,
    knobCompletedRadius,
    knobCompletedWidth,
    knobCurrentBackgroundColor,
    knobCurrentBorderColor,
    knobCurrentBorderWidth,
    knobCurrentPaddingLeft,
    knobCurrentPaddingTop,
    knobSize
  },
  isCompleted,
  isCurrent
) => ({
  backgroundColor: knobBackgroundColor,
  borderColor: knobBorderColor,
  borderRadius: knobSize / FULL_ROUND_DIVISOR,
  borderWidth: knobBorderWidth,
  height: knobSize,
  width: knobSize,
  ...(isCompleted && {
    backgroundColor: knobCurrentBackgroundColor,
    borderColor: knobCurrentBorderColor,
    borderWidth: knobCurrentBorderWidth,
    paddingLeft: knobCurrentPaddingLeft,
    paddingTop: knobCurrentPaddingTop
  }),
  ...(isCurrent && {
    backgroundColor: knobCompletedBackgroundColor,
    borderColor: knobCompletedBorderColor,
    borderWidth: knobCurrentBorderWidth,
    paddingLeft: knobCompletedPaddingLeft,
    paddingTop: knobCompletedPaddingTop,
    width: knobCompletedWidth || knobSize,
    borderRadius: knobCompletedRadius || knobSize / FULL_ROUND_DIVISOR
  })
})
const selectLabelContainerStyles = ({
  labelDirection,
  labelGap,
  labelMarginTop,
  labelPaddingLeft,
  labelPaddingRight
}) => ({
  marginTop: labelMarginTop,
  paddingLeft: labelPaddingLeft,
  paddingRight: labelPaddingRight,
  flexDirection: labelDirection,
  gap: labelGap
})
const selectStepLabelStyles = (
  {
    stepLabelColor,
    labelCurrentColor,
    stepLabelFontWeight,
    stepLabelFontSize,
    stepLabelFontName,
    stepLabelLineHeight
  },
  themeOptions,
  isCurrent
) =>
  applyTextStyles({
    color: isCurrent ? labelCurrentColor : stepLabelColor,
    fontSize: stepLabelFontSize,
    lineHeight: stepLabelLineHeight,
    fontWeight: stepLabelFontWeight,
    fontName: stepLabelFontName,
    themeOptions
  })
const selectLabelStyles = (
  {
    labelColor,
    labelCurrentColor,
    labelCurrentFontWeight,
    labelFontSize,
    labelFontWeight,
    labelFontName,
    labelLineHeight
  },
  themeOptions,
  isCurrent
) =>
  applyTextStyles({
    color: isCurrent ? labelCurrentColor : labelColor,
    fontSize: labelFontSize,
    lineHeight: labelLineHeight,
    fontWeight: isCurrent ? labelCurrentFontWeight : labelFontWeight,
    fontName: labelFontName,
    themeOptions
  })
const getStepTestID = (isCompleted, isCurrent) => {
  let testID = 'StepTracker-Step'
  if (isCompleted) {
    testID += '-Completed'
  } else if (isCurrent) {
    testID += '-Current'
  }

  return testID
}
const selectBarContainerStyles = (themeTokens, isCompleted, isCurrent) => ({
  backgroundColor: isCompleted
    ? themeTokens.barCompletedBackgroundColor
    : themeTokens.barBackgroundColor,
  height: themeTokens.barHeight,
  ...(isCurrent && {
    backgroundColor: themeTokens.barCurrentBackgroundColor
  })
})

/**
 * A single step of a StepTracker.
 *
 * When the theme enables `knobAnimatePill` (the koodo carousel floating style), the active dot
 * runs a circle⇄pill spring morph driven by `pillProgress` and respects the reduced-motion
 * setting. When the token is off — every other brand and the standalone StepTracker — the knob
 * renders as a plain static View, identical to its previous behaviour.
 */
export const Step = React.forwardRef(
  (
    {
      label,
      name,
      status = 0,
      stepCount = 0,
      stepIndex = 0,
      tokens,
      isBarVariant,
      onStepPress,
      pressLabel,
      ...rest
    },
    ref
  ) => {
    const { completedIcon, showStepLabel, showStepName } = tokens
    const themeTokens = React.useMemo(() => {
      const {
        completedIcon: _completedIcon,
        showStepLabel: _showStepLabel,
        showStepName: _showStepName,
        textStepTrackerLabel: _textStepTrackerLabel,
        ...styleTokens
      } = tokens
      return styleTokens
    }, [tokens])
    const isFirst = stepIndex === 0
    const isLast = stepIndex === stepCount - 1
    const isCompleted = status > stepIndex
    const isCurrent = status === stepIndex
    const isActive = isCompleted || isCurrent
    const { themeOptions } = useTheme()

    const animatePill = Boolean(themeTokens.knobAnimatePill)
    const { reduceMotionEnabled } = useA11yInfo()
    const pillTargetValue = isCurrent ? 1 : 0
    const pillProgress = React.useRef(new Animated.Value(pillTargetValue)).current

    React.useEffect(() => {
      if (!animatePill) return undefined
      if (reduceMotionEnabled) {
        pillProgress.setValue(pillTargetValue)
        return undefined
      }
      const animation = Animated.spring(pillProgress, {
        toValue: pillTargetValue,
        ...PILL_SPRING,
        useNativeDriver: false
      })
      animation.start()
      return () => animation.stop()
    }, [animatePill, pillTargetValue, reduceMotionEnabled, pillProgress])

    const KnobComponent = animatePill ? Animated.View : View
    const animatedKnobStyle = React.useMemo(
      () => (animatePill ? selectAnimatedKnobStyle(pillProgress, themeTokens) : false),
      [animatePill, pillProgress, themeTokens]
    )
    const knobStyle = React.useMemo(
      () => selectKnobStyles(themeTokens, isCompleted, isCurrent),
      [themeTokens, isCompleted, isCurrent]
    )

    const isPressable = typeof onStepPress === 'function'

    const knob = (
      <KnobComponent
        style={
          animatePill
            ? [staticStyles.knob, knobStyle, animatedKnobStyle]
            : [staticStyles.knob, knobStyle]
        }
        testID={getStepTestID(isCompleted, isCurrent)}
      >
        {isCompleted && completedIcon && (
          <Icon icon={completedIcon} tokens={selectCompletedIconTokens(themeTokens)} />
        )}
        {isCurrent && <View style={selectCurrentInnerStyles(themeTokens)} />}
      </KnobComponent>
    )

    return (
      <StackView
        space={0}
        tokens={{ alignItems: 'stretch', flexGrow: 1 }}
        accessibilityLabel={label}
        accessibilityLevel={2}
        accessibilityCurrent={status === stepIndex}
        ref={ref}
        {...selectProps(rest)}
      >
        {isBarVariant && (
          <View
            style={selectBarContainerStyles(themeTokens, isCompleted, isCurrent)}
            testID={getStepTestID(isCompleted, isCurrent)}
          />
        )}
        {!isBarVariant && (
          <StackView
            direction="row"
            space={0}
            tokens={{ alignItems: 'center', flexGrow: 0, justifyContent: 'center' }}
          >
            <View
              style={[
                staticStyles.connector,
                !isFirst && selectConnectorStyles(themeTokens, isActive)
              ]}
            />
            {isPressable ? (
              <Pressable
                onPress={() => onStepPress?.(stepIndex)}
                onKeyDown={(event) => {
                  if (event?.key === ' ') onStepPress?.(stepIndex)
                }}
                accessibilityRole="button"
                accessibilityLabel={pressLabel ?? label}
                accessibilityState={{ selected: isCurrent }}
                hitSlop={KNOB_HIT_SLOP}
              >
                {knob}
              </Pressable>
            ) : (
              knob
            )}
            <View
              style={[
                staticStyles.connector,
                !isLast && selectConnectorStyles(themeTokens, isCompleted)
              ]}
            />
          </StackView>
        )}
        {showStepLabel && (
          <View style={[staticStyles.stepLabelContainer, selectLabelContainerStyles(tokens)]}>
            {showStepName && (
              <Text
                style={[
                  staticStyles.centeredText,
                  selectStepLabelStyles(tokens, themeOptions, isCurrent)
                ]}
              >
                {name}
              </Text>
            )}
            <StackView direction="row">
              <Text
                style={[
                  staticStyles.centeredText,
                  tokens.labelDirection === 'column' && staticStyles.wrappingLabel,
                  selectLabelStyles(tokens, themeOptions, isCurrent)
                ]}
              >
                {label}
              </Text>
            </StackView>
          </View>
        )}
      </StackView>
    )
  }
)

Step.displayName = 'Step'

Step.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * The step's title, used as its accessible label.
   */
  label: PropTypes.string.isRequired,
  /**
   * The visible step name shown above the label when `showStepName` is enabled.
   */
  name: PropTypes.string.isRequired,
  /**
   * The StepTracker's current step (0-based); compared against `stepIndex` to derive this step's state.
   */
  status: PropTypes.number.isRequired,
  /**
   * Total number of steps, used to detect the last step.
   */
  stepCount: PropTypes.number.isRequired,
  /**
   * This step's 0-based position within the StepTracker.
   */
  stepIndex: PropTypes.number.isRequired,
  /**
   * Resolved StepTracker theme tokens for this step; drives the knob, connector, and label styling.
   */
  tokens: getTokensPropType('StepTracker'),
  /**
   * Renders the step as a filled bar segment (the bar StepTracker style) instead of the default
   * dot-and-connector layout.
   */
  isBarVariant: PropTypes.bool,
  /**
   * Appearance selection used to resolve variant-specific tokens (e.g. `inverse`, `floating`).
   */
  variant: variantProp.propType,
  /**
   * Called with this step's 0-based index when pressed. Providing it renders the knob as a `button`.
   */
  onStepPress: PropTypes.func,
  /**
   * Accessible label for the step button. Only used when `onStepPress` is provided.
   */
  pressLabel: PropTypes.string
}

const staticStyles = StyleSheet.create({
  centeredText: { textAlign: 'center' },
  connector: { flex: 1, height: 1 },
  completedKnob: {
    backgroundColor: 'transparent',
    textAlign: 'center'
  },
  knob: { borderWidth: 1, display: 'flex', justifyContent: 'center', alignItems: 'center' },
  stepLabelContainer: { justifyContent: 'center' },
  wrappingLabel: { width: 0, flex: 1 }
})
