import { tva } from '@gluestack-ui/nativewind-utils/tva'
import { VariantProps } from '@gluestack-ui/nativewind-utils/types'
import { ComponentRef, FC, forwardRef, useEffect, useState } from 'react'
import { Pressable, TouchableOpacity } from 'react-native'
import Animated, {
  Extrapolation,
  interpolate,
  interpolateColor,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated'

import { variables } from '@/figma-vars'

const sizes = {
  md: {
    switchWidth: variables['[selectors]']['switch-track-width'],
    switchBorderSize: variables['[selectors]']['switch-track-border-size'],
    switchThumbContainerSize: variables['[selectors]']['switch-thumb-size-false'],
    switchThumbContainerOffset: variables['[selectors]']['switch-track-padding-left'],
  },
  lg: {
    switchWidth: variables['[selectors]']['lg-switch-track-width'],
    switchBorderSize: variables['[selectors]']['lg-switch-track-border-size'],
    switchThumbContainerSize: variables['[selectors]']['lg-switch-thumb-size-false'],
    switchThumbContainerOffset: variables['[selectors]']['lg-switch-track-padding-left'],
  },
}

const switchStyles = tva({
  base: 'justify-center border-[var(--color-selector-border)] bg-[var(--color-selector-track-background)]',
  variants: {
    size: {
      md: 'h-[var(--switch-track-height)] w-[var(--switch-track-width)] rounded-[var(--switch-track-radius)] border-[length:var(--switch-track-border-size)]',
      lg: 'h-[var(--lg-switch-track-height)] w-[var(--lg-switch-track-width)] rounded-[var(--lg-switch-track-radius)] border-[length:var(--lg-switch-track-border-size)]',
    },
  },
})

const switchThumbStyles = tva({
  base: 'rounded-full border-[length:var(--switch-thumb-border-size)] border-[var(--color-selector-border-thumb-false)] bg-[var(--color-selector-thumb-false)]',
  variants: {
    size: {
      md: 'h-[var(--switch-thumb-size-false)] w-[var(--switch-thumb-size-false)]',
      lg: 'h-[var(--lg-switch-thumb-size-false)] w-[var(--lg-switch-thumb-size-false)]',
    },
  },
})

type SwitchProps = VariantProps<typeof switchStyles> & {
  isChecked?: boolean
  value?: string | number
  onChange?: (isChecked: boolean) => void
  className?: string
  isDisabled?: boolean
}

const AnimatedSwitch: FC<SwitchProps & { pressed: boolean }> = ({ size = 'md', isDisabled, isChecked, pressed }) => {
  const isOn = useSharedValue(isChecked ? 1 : 0)

  useEffect(() => {
    isOn.value = withTiming(pressed ? 0.5 : isChecked ? 1 : 0, { duration: 200 })
  }, [isOn, isChecked, pressed])

  const { switchWidth, switchThumbContainerSize, switchThumbContainerOffset, switchBorderSize } = sizes[size]

  const animatedTrackStyle = useAnimatedStyle(() => {
    return {
      backgroundColor: interpolateColor(
        isOn.value,
        [0, 0.5, 1],
        [
          isDisabled ? variables['theme-color']['disabled'] : variables['theme-color']['selector-track-background'],
          variables['theme-color']['selector-accent-onTap'],
          isDisabled ? variables['theme-color']['disabled-dark'] : variables['theme-color']['selector-accent-true'],
        ],
      ),
      borderColor: interpolateColor(
        isOn.value,
        [0, 0.5, 1],
        [
          isDisabled ? variables['theme-color']['disabled-border'] : variables['theme-color']['selector-border'],
          variables['theme-color']['selector-border-onTap'],
          isDisabled
            ? variables['theme-color']['disabled-border-ondark']
            : variables['theme-color']['selector-border-active'],
        ],
      ),
    }
  }, [isDisabled])

  const animatedThumbStyle = useAnimatedStyle(() => {
    const inactiveOffset = Number(switchThumbContainerOffset)
    const activeOffset =
      Number(switchWidth) -
      Number(switchThumbContainerSize) -
      Number(switchThumbContainerOffset) -
      Number(switchBorderSize) * 2

    const middleOffset = inactiveOffset + (activeOffset - inactiveOffset) / 2

    return {
      transform: [
        {
          translateX: interpolate(
            isOn.value,
            [0, 0.5, 1],
            [inactiveOffset, middleOffset, activeOffset],
            Extrapolation.CLAMP,
          ),
        },
      ],
    }
  }, [switchWidth, switchThumbContainerOffset, switchThumbContainerSize, switchBorderSize])

  return (
    <Animated.View className={switchStyles({ size })} style={animatedTrackStyle}>
      <Animated.View
        style={animatedThumbStyle}
        className={switchThumbStyles({
          size,
        })}
      />
    </Animated.View>
  )
}

const Switch = forwardRef<ComponentRef<typeof TouchableOpacity>, SwitchProps>(function Switch(
  { className, size = 'md', onChange, ...props },
  ref,
) {
  const [isCheckedLocally, setIsCheckedLocally] = useState(false)

  const isChecked = props.isChecked ?? isCheckedLocally

  const handleChange = () => {
    if (onChange) {
      onChange(!isChecked)
    } else {
      setIsCheckedLocally(!isChecked)
    }
  }

  return (
    <Pressable onPress={handleChange} ref={ref} disabled={props.isDisabled} className={className}>
      {({ pressed }) => <AnimatedSwitch {...props} pressed={pressed} isChecked={isChecked} />}
    </Pressable>
  )
})

Switch.displayName = 'Switch'

export { Switch }
