import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback, createElement } from 'react';
import { StyleSheet, View } from 'react-native';
import Reanimated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, isHarmony, extendObject } from '../utils';
import useNodesRef from '../useNodesRef';
import PickerIndicator from './pickerViewIndicator';
import PickerMask from './pickerViewMask';
import MpxPickerViewColumnItem from './pickerViewColumnItem';
import MpxPickerViewColumnItemLite from './pickerViewColumnItemLite';
import { PickerViewColumnAnimationContext } from '../mpx-picker-view/pickerVIewContext';
import { calcHeightOffsets } from './pickerViewFaces';
const visibleCount = 5;
const _PickerViewColumn = forwardRef((props, ref) => {
    const { columnData, columnIndex, initialIndex, onSelectChange, style, wrapperStyle, pickerMaskStyle, pickerIndicatorStyle, enableWheelAnimation = true, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props;
    const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext });
    const { textStyle = {} } = splitStyle(normalStyle);
    const { textProps = {} } = splitProps(props);
    const scrollViewRef = useAnimatedRef();
    const offsetYShared = useScrollViewOffset(scrollViewRef);
    useNodesRef(props, ref, scrollViewRef, {
        style: normalStyle
    });
    const { height: pickerH, itemHeight } = wrapperStyle;
    const [itemRawH, setItemRawH] = useState(itemHeight);
    const maxIndex = useMemo(() => columnData.length - 1, [columnData]);
    const prevScrollingInfo = useRef({ index: initialIndex, y: 0 });
    const dragging = useRef(false);
    const scrolling = useRef(false);
    const timerResetPosition = useRef(null);
    const timerScrollTo = useRef(null);
    const timerClickOnce = useRef(null);
    const activeIndex = useRef(initialIndex);
    const prevIndex = usePrevious(initialIndex);
    const prevMaxIndex = usePrevious(maxIndex);
    const { layoutProps } = useLayout({
        props,
        hasSelfPercent,
        setWidth,
        setHeight,
        nodeRef: scrollViewRef
    });
    const paddingHeight = useMemo(() => Math.round((pickerH - itemHeight) / 2), [pickerH, itemHeight]);
    const snapToOffsets = useMemo(() => Array.from({ length: maxIndex + 1 }, (_, i) => i * itemRawH), [maxIndex, itemRawH]);
    const contentContainerStyle = useMemo(() => {
        return [{ paddingVertical: paddingHeight }];
    }, [paddingHeight]);
    const initialContentOffsetY = useMemo(() => initialIndex * itemRawH, [initialIndex, itemRawH]);
    const getIndex = useCallback((y) => {
        const calc = Math.round(y / itemRawH);
        return Math.max(0, Math.min(calc, maxIndex));
    }, [itemRawH, maxIndex]);
    const clearTimerResetPosition = useCallback(() => {
        if (timerResetPosition.current) {
            clearTimeout(timerResetPosition.current);
            timerResetPosition.current = null;
        }
    }, []);
    const clearTimerScrollTo = useCallback(() => {
        if (timerScrollTo.current) {
            clearTimeout(timerScrollTo.current);
            timerScrollTo.current = null;
        }
    }, []);
    const clearTimerClickOnce = useCallback(() => {
        if (timerClickOnce.current) {
            clearTimeout(timerClickOnce.current);
            timerClickOnce.current = null;
        }
    }, []);
    useEffect(() => {
        return () => {
            clearTimerResetPosition();
            clearTimerScrollTo();
        };
    }, []);
    useEffect(() => {
        if (!scrollViewRef.current ||
            !itemRawH ||
            dragging.current ||
            scrolling.current ||
            prevIndex == null ||
            initialIndex === prevIndex ||
            initialIndex === activeIndex.current ||
            maxIndex !== prevMaxIndex) {
            return;
        }
        clearTimerScrollTo();
        timerScrollTo.current = setTimeout(() => {
            scrollViewRef.current?.scrollTo({
                x: 0,
                y: initialIndex * itemRawH,
                animated: false
            });
            activeIndex.current = initialIndex;
        }, isIOS ? 0 : 200);
    }, [itemRawH, maxIndex, initialIndex]);
    const onItemLayout = useCallback((e) => {
        const { height: rawH } = e.nativeEvent.layout;
        const roundedH = Math.round(rawH);
        if (roundedH && roundedH !== itemRawH) {
            setItemRawH(roundedH);
        }
    }, [itemRawH]);
    const resetScrollPosition = useCallback((y) => {
        if (dragging.current || scrolling.current) {
            return;
        }
        scrolling.current = true;
        const targetIndex = getIndex(y);
        scrollViewRef.current?.scrollTo({ x: 0, y: targetIndex * itemRawH, animated: false });
    }, [itemRawH, getIndex]);
    const onMomentumScrollBegin = useCallback(() => {
        isIOS && clearTimerResetPosition();
        scrolling.current = true;
    }, []);
    const onMomentumScrollEnd = useCallback((e) => {
        scrolling.current = false;
        const { y: scrollY } = e.nativeEvent.contentOffset;
        if (isIOS && scrollY % itemRawH !== 0) {
            return resetScrollPosition(scrollY);
        }
        const calcIndex = getIndex(scrollY);
        if (calcIndex !== activeIndex.current) {
            activeIndex.current = calcIndex;
            onSelectChange(calcIndex);
        }
    }, [itemRawH, getIndex, onSelectChange, resetScrollPosition]);
    const onScrollBeginDrag = useCallback(() => {
        isIOS && clearTimerResetPosition();
        dragging.current = true;
        prevScrollingInfo.current = {
            index: activeIndex.current,
            y: activeIndex.current * itemRawH
        };
    }, [itemRawH]);
    const onScrollEndDrag = useCallback((e) => {
        dragging.current = false;
        if (!isAndroid) {
            const { y } = e.nativeEvent.contentOffset;
            if (y % itemRawH === 0 || (isHarmony && y > snapToOffsets[maxIndex])) {
                onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } });
            }
            else if (y > 0 && y < snapToOffsets[maxIndex]) {
                timerResetPosition.current = setTimeout(() => {
                    resetScrollPosition(y);
                }, 10);
            }
        }
    }, [itemRawH, maxIndex, snapToOffsets, onMomentumScrollEnd, resetScrollPosition]);
    const onScroll = useCallback((e) => {
        // 全局注册的振动触感 hook
        const onPickerVibrate = global.__mpx?.config?.rnConfig?.onPickerVibrate;
        if (typeof onPickerVibrate !== 'function') {
            return;
        }
        const { y } = e.nativeEvent.contentOffset;
        const { index: prevIndex, y: _y } = prevScrollingInfo.current;
        if (dragging.current || scrolling.current) {
            if (Math.abs(y - _y) >= itemRawH) {
                const currentId = getIndex(y);
                if (currentId !== prevIndex) {
                    prevScrollingInfo.current = {
                        index: currentId,
                        y: currentId * itemRawH
                    };
                    // vibrateShort({ type: 'selection' })
                    onPickerVibrate();
                }
            }
        }
    }, [itemRawH, getIndex]);
    const offsetHeights = useMemo(() => calcHeightOffsets(itemRawH), [itemRawH]);
    const calcOffset = useCallback((y) => {
        const baselineY = activeIndex.current * itemRawH + pickerH / 2;
        const diff = Math.abs(y - baselineY);
        const positive = y - baselineY > 0 ? 1 : -1;
        const [h1, h2, h3] = offsetHeights;
        if (diff > h1 && diff < h3) {
            if (diff < h2) {
                return 1 * positive;
            }
            else {
                return 2 * positive;
            }
        }
        return false;
    }, [offsetHeights]);
    /**
     * 和小程序表现对齐，点击（不滑动）非焦点选项自动滚动到对应位置
     */
    const onClickOnceItem = useCallback((e) => {
        const { locationY } = e.nativeEvent || {};
        const offsetIndex = calcOffset(locationY);
        if (dragging.current || !offsetIndex) {
            return;
        }
        const targetIndex = activeIndex.current + offsetIndex;
        if (targetIndex < 0 || targetIndex > maxIndex) {
            return;
        }
        const y = targetIndex * itemRawH;
        scrollViewRef.current?.scrollTo({ x: 0, y, animated: true });
        if (isAndroid) {
            // Android scrollTo 不会自动触发 onMomentumScrollEnd，需要手动触发
            clearTimerClickOnce();
            timerClickOnce.current = setTimeout(() => {
                onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } });
            }, 250);
        }
    }, [itemRawH, maxIndex, calcOffset, onMomentumScrollEnd]);
    const renderInnerchild = () => columnData.map((item, index) => {
        return enableWheelAnimation
            ? (<MpxPickerViewColumnItem key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout}/>)
            : (<MpxPickerViewColumnItemLite key={index} item={item} index={index} itemHeight={itemHeight} textStyle={textStyle} textProps={textProps} onItemLayout={onItemLayout}/>);
    });
    const renderScollView = () => {
        const innerProps = extendObject({}, layoutProps, {
            ref: scrollViewRef,
            bounces: true,
            horizontal: false,
            nestedScrollEnabled: true,
            removeClippedSubviews: false,
            showsVerticalScrollIndicator: false,
            showsHorizontalScrollIndicator: false,
            scrollEventThrottle: 16,
            style: styles.scrollView,
            decelerationRate: 'fast',
            snapToOffsets: snapToOffsets,
            onTouchEnd: onClickOnceItem,
            onScroll,
            onScrollBeginDrag,
            onScrollEndDrag,
            onMomentumScrollBegin,
            onMomentumScrollEnd,
            contentContainerStyle,
            contentOffset: { x: 0, y: initialContentOffsetY }
        });
        return createElement(PickerViewColumnAnimationContext.Provider, { value: offsetYShared }, createElement(Reanimated.ScrollView, innerProps, renderInnerchild()));
    };
    const renderIndicator = () => (<PickerIndicator itemHeight={itemHeight} indicatorItemStyle={pickerIndicatorStyle}/>);
    const renderMask = () => (<PickerMask itemHeight={itemHeight} maskContainerStyle={pickerMaskStyle}/>);
    return (<View style={[styles.wrapper, normalStyle]}>
      {renderScollView()}
      {renderMask()}
      {renderIndicator()}
    </View>);
});
const styles = StyleSheet.create({
    wrapper: { display: 'flex', flex: 1 },
    scrollView: { width: '100%' }
});
_PickerViewColumn.displayName = 'MpxPickerViewColumn';
export default _PickerViewColumn;
