import React, { useImperativeHandle, useMemo, useRef, useState, Ref } from 'react'
import { Easing, ScrollView } from 'react-native'
import { Text } from '../Text'
import { View } from '../View'
import { useAnimatedVariantStyles } from '../../utils'
import { SegmentedControlOption } from './Option'
import { SegmentedControlProps, SegmentedControlRef } from './types'
import { AnyRecord, IJSX, StyledComponentProps, StyledComponentWithProps, useTheme } from '@codeleap/styles'
import { MobileStyleRegistry } from '../../Registry'
import { useStylesFor } from '../../hooks'

export * from './styles'
export * from './types'

const DefaultBubble = (props) => {
  return <View animated {...props} />
}

const defaultAnimation = {
  type: 'timing',
  duration: 200,
  easing: Easing.linear,
}

/**
 * Always fully controlled — there is no internal selected state; `value` and `onValueChange`
 * are required. The animated bubble position is driven by Reanimated worklets, so the bubble
 * can only animate when `currentOptionIdx` changes (i.e. `value` changes from the parent).
 */
export const SegmentedControl = React.forwardRef<SegmentedControlRef, SegmentedControlProps>((props, ref) => {
  const [themeValues, themeSpacing] = useTheme(store => [store.theme?.values as any, store.theme?.spacing])

  const {
    options = [],
    onValueChange,
    debugName,
    label,
    value,
    animation = {},
    scrollProps = {},
    /** Default divides total screen width equally; override `getItemWidth` when options have variable label lengths or the control doesn't fill the screen. */
    getItemWidth = () => (themeValues?.width - themeSpacing?.value?.(4)) / options.length,
    renderBubble: BubbleView,
    scrollToCurrentOptionOnMount,
    renderOption: Option,
    touchableProps,
    style,
    ...viewProps
  } = {
    ...SegmentedControl.defaultProps,
    ...props,
  }

  const [bubbleWidth, setBubbleWidth] = useState(0)

  const _animation = {
    ...defaultAnimation,
    ...animation,
  }

  const styles = useStylesFor(SegmentedControl.styleRegistryName, style)

  const scrollRef = useRef<SegmentedControlRef>(null)

  function scrollTo(idx: number) {
    if (!scrollRef.current) return
    setTimeout(() => {
      scrollRef.current?.scrollTo({
        x: widthStyle.width * idx, 
        y: 0,
        animated: true
      })

    })
  }

  const widthStyle = useMemo(() => {
    if (getItemWidth) {
      const sizes = options.map(getItemWidth)
      const maxWidth = sizes.sort((a, b) => b - a)[0]

      return { width: maxWidth }

    }

    return {
      width: bubbleWidth,
    }
  }, [options, bubbleWidth])

  const currentOptionIdx = options.findIndex(o => o.value === value) || 0

  const onPress = (txt: string, idx: number) => {
    return () => {
      onValueChange(txt)
      scrollTo(idx)
    }
  }

  const hasScrolledInitially = useRef(false)

  useImperativeHandle(ref, () => {
    if (!scrollRef.current) return null

    return {
      ...(scrollRef.current),
      scrollTo,
      scrollToCurrent() {
        if (!scrollRef.current) return
        scrollTo(currentOptionIdx)
      },
    } as SegmentedControlRef
  }, [
    currentOptionIdx,
  ])

  if (!hasScrolledInitially.current && scrollRef.current && scrollToCurrentOptionOnMount) {
    scrollTo(currentOptionIdx)
    hasScrolledInitially.current = true
  }

  const bubbleAnimation = useAnimatedVariantStyles({
    variantStyles: styles,
    animatedProperties: [],
    updater: () => {
      'worklet'
      return {
        translateX: currentOptionIdx * widthStyle.width,
      }
    },
    transition: _animation,
    dependencies: [currentOptionIdx, widthStyle.width],
  })

  /** Tracks the widest option measured during onLayout to set a uniform bubble width when `getItemWidth` is not provided; reset to 0 after each full pass so re-renders don't accumulate stale values. */
  const largestWidth = useRef(0)

  return (
    <View style={styles?.wrapper}>
      {label ? (
        <View {...viewProps} style={styles?.labelWrapper}>
          <Text style={styles?.labelText} text={label} />
        </View>
      ) : null}

      <ScrollView
        horizontal
        showsHorizontalScrollIndicator={false}
        style={styles?.scroll}
        contentContainerStyle={styles?.scrollContent}
        {...scrollProps}
        ref={scrollRef as unknown as Ref<ScrollView>}
      >
        <View style={styles?.innerWrapper}>
          <BubbleView
            options={options}
            animated
            animatedStyle={bubbleAnimation}
            style={[
              styles?.selectedBubble,
              props?.touchableProps?.disabled && styles?.['selectedBubble:disabled'],
              widthStyle,
            ]}
          />
          {options.map((o, idx) => (
            <Option
              debugName={debugName}
              label={o.label}
              value={o.value}
              icon={o.icon}
              onPress={onPress(o.value, idx)}
              key={idx}
              style={widthStyle}
              selected={value === o.value}
              variantStyles={styles}
              onLayout={e => {
                const { width } = e.nativeEvent.layout
                if (width > largestWidth.current) {
                  largestWidth.current = width
                }

                if (idx === options.length - 1) {
                  setBubbleWidth(largestWidth.current)
                  largestWidth.current = 0
                }
              }}
              {...touchableProps}
            />
          ))}
        </View>
      </ScrollView>
    </View>
  )
}) as StyledComponentWithProps<SegmentedControlProps>

SegmentedControl.styleRegistryName = 'SegmentedControl'
SegmentedControl.elements = ['wrapper', 'selectedBubble', 'innerWrapper', 'scroll', 'text', 'icon', 'button', 'label', 'badge']
SegmentedControl.rootElement = 'scroll'

SegmentedControl.withVariantTypes = <S extends AnyRecord>(styles: S) => {
  return SegmentedControl as (<T = string>(props: StyledComponentProps<SegmentedControlProps<T> & { ref?: React.Ref<SegmentedControlRef> }, typeof styles>) => IJSX)
}

SegmentedControl.defaultProps = {
  renderBubble: DefaultBubble,
  renderOption: SegmentedControlOption,
  scrollToCurrentOptionOnMount: true,
} as Partial<SegmentedControlProps>

MobileStyleRegistry.registerComponent(SegmentedControl)
