import React, { Children, cloneElement, Fragment, useState } from 'react';
import { View, StyleSheet, TouchableOpacity } from 'react-native';
import { Binding } from '../../utils/binding';
import { Modifiers, WithChildren } from '../../utils/modifiers';
import Animated, {
  runOnJS,
  useAnimatedGestureHandler,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';
import {
  PanGestureHandler,
  PanGestureHandlerGestureEvent,
} from 'react-native-gesture-handler';
import {
  clamp,
  GestureHandlerContext,
  positionToSelected,
  selectedToPosition,
} from './utils';
import { getCornerRadius } from '../../utils/cornerRadius';
import { getShadow } from '../../utils/shadow';
import { getPadding } from '../../utils/padding';
import { getFrame } from '../../utils/frame';
import { getBorder } from '../../utils/border';
import { useLifecycle } from '../../hooks/useLifecycle';
import { useColorScheme } from '../../hooks/useColorScheme';
import { Text } from '../Text';
import { getTransform } from '../../utils/transform';
import { getColor } from '../../utils/colors';
import { useAlert } from '../../hooks/useAlert';
import { ReactElement } from 'react';

type PickerProps = Modifiers &
  WithChildren & {
    selection: Binding<number>;
    onChange?: (value: number) => void;
  };

export const Picker = ({
  children,
  selection,
  backgroundColor,
  opacity,
  frame,
  cornerRadius,
  scaleEffect,
  rotationEffect,
  padding,
  border,
  shadow,
  zIndex,
  style,
  alert,
  onChange,
  onAppear,
  onDisappear,
}: PickerProps) => {
  useAlert(alert);
  useLifecycle(onAppear, onDisappear);
  const colorScheme = useColorScheme();
  const childCount = Children.count(children);
  const [optionDimensions, setOptionDimensions] = useState(null);
  const tempSelection = useSharedValue(selection.value);
  const width = optionDimensions ? optionDimensions.width : 0;
  const slidePosition = useSharedValue(
    selectedToPosition(selection.value, width, childCount)
  );

  const sliderHeight = optionDimensions ? optionDimensions.height - 5 : 0;
  const sliderWidth = optionDimensions
    ? optionDimensions.width / childCount
    : 0;

  const animatedSliderStyle = useAnimatedStyle(() => {
    return {
      transform: [{ translateX: slidePosition.value }],
    };
  }, [slidePosition.value]);

  const gestureHandler = useAnimatedGestureHandler<
    PanGestureHandlerGestureEvent,
    GestureHandlerContext
  >({
    onStart: (_, ctx) => {
      ctx.offsetX = slidePosition.value;
    },
    onActive: (e, ctx) => {
      const currentXPos = e.translationX + ctx.offsetX;
      const optionWidth = width / childCount;
      const slideTo = clamp(
        Math.round((currentXPos - optionWidth / 2) / optionWidth) * optionWidth,
        0,
        (childCount - 1) * optionWidth
      );
      if (slideTo !== slidePosition.value) {
        slidePosition.value = withTiming(slideTo);
        tempSelection.value = positionToSelected(slideTo, width, childCount);
        console.log(tempSelection.value);
      }
    },
    onEnd: () => {
      const newValue = positionToSelected(
        slidePosition.value,
        width,
        childCount
      );

      runOnJS(selection.setValue)(newValue);
      if (onChange) {
        runOnJS(onChange)(newValue);
      }
    },
  });

  return (
    <Fragment>
      <View
        onLayout={(e) => setOptionDimensions(e.nativeEvent.layout)}
        style={[
          styles.container,
          {
            opacity,
            zIndex,
            backgroundColor: getColor(
              backgroundColor,
              colorScheme,
              'secondarySystemBackground'
            ),
            ...getCornerRadius(cornerRadius),
            ...getShadow(shadow),
            ...getPadding(padding),
            ...getFrame(frame),
            ...getBorder(border),
            ...getTransform(scaleEffect, rotationEffect),
          },
          style,
        ]}
      >
        <View style={styles.options}>
          {React.Children.map(children as ReactElement<any>, (child, i) => {
            const textChild =
              child?.type === Text
                ? cloneElement(child, { fontSize: 12, ...child.props })
                : child;
            return (
              <Fragment key={i}>
                <TouchableOpacity
                  style={[styles.option, { flexBasis: `${100 / childCount}%` }]}
                  disabled={selection.value === i}
                  onPress={() => {
                    slidePosition.value = withTiming(
                      selectedToPosition(i, width, childCount)
                    );
                    tempSelection.value = i;
                    runOnJS(selection.setValue)(i);
                    if (onChange) {
                      runOnJS(onChange)(i);
                    }
                  }}
                >
                  {textChild}
                </TouchableOpacity>
                <Divider
                  color={getColor('systemGray4', colorScheme)}
                  index={i}
                  selection={tempSelection.value}
                  childCount={childCount}
                />
              </Fragment>
            );
          })}
        </View>
        <Animated.View
          style={[
            styles.slider,
            animatedSliderStyle,
            {
              width: sliderWidth,
              height: sliderHeight,
              backgroundColor:
                colorScheme === 'dark'
                  ? getColor('secondarySystemBackground', 'dark')
                  : getColor('systemBackground', 'light'),
            },
          ]}
        />
      </View>

      <PanGestureHandler onGestureEvent={gestureHandler}>
        <Animated.View
          style={[
            styles.slider,
            animatedSliderStyle,
            {
              width: sliderWidth,
              height: sliderHeight,
              zIndex: 0,
              left: 10,
              top: 12,
            },
          ]}
        />
      </PanGestureHandler>
    </Fragment>
  );
};

const Divider = ({
  color,
  index,
  selection,
  childCount,
}: {
  color: string;
  index: number;
  selection: number;
  childCount: number;
}) => {
  const animatedDividerStyle = useAnimatedStyle(() => {
    return {
      opacity:
        index === selection ||
        index === selection - 1 ||
        index === childCount - 1
          ? withTiming(0)
          : withTiming(1),
    };
  }, [index, selection, childCount]);

  return (
    <Animated.View
      style={[
        styles.divider,
        animatedDividerStyle,
        {
          borderRightColor: color,
        },
      ]}
    />
  );
};

const styles = StyleSheet.create({
  container: {
    borderRadius: 6,
    flexDirection: 'row',
    padding: 3,
  },
  options: {
    flexDirection: 'row',
    justifyContent: 'center',
  },
  option: {
    justifyContent: 'center',
    paddingVertical: 5,
    paddingHorizontal: 20,
  },
  divider: {
    top: 5,
    height: 15,
    borderRightWidth: 1,
    width: 0,
  },
  slider: {
    position: 'absolute',
    top: 2,
    zIndex: -1,
    borderRadius: 6,
    shadowColor: 'black',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.1,
    shadowRadius: 3,
  },
});