import {
  memo,
  PropsWithChildren,
  RefObject,
  useImperativeHandle,
  useRef,
} from 'react';
import {
  findNodeHandle,
  NativeModules,
  requireNativeComponent,
  StyleProp,
  UIManager,
  ViewProps,
  ViewStyle,
} from 'react-native';
import {isAndroid} from '@shared/utils';

export interface SwipeableRef {
  close: () => void;
}
interface NativeSwipeableProps extends ViewProps {
  style?: StyleProp<ViewStyle>;
  /**
   * Maximum horizontal offset (in points) when swiping left.
   * Defines how far the content can be translated to reveal right-side actions.
   * @default 75
   */
  maxLeftOffset?: number;
  /**
   * Maximum horizontal offset (in points) when swiping right.
   * Defines how far the content can be translated to reveal left-side actions.
   * @default 75
   */
  maxRightOffset?: number;
  /**
   * Horizontal distance (in points) required to activate
   * the "open" state when swiping to the right.
   *
   * Once this threshold is reached, the component will
   * animate to maxRightOffset.
   * @default 30
   */
  thresholdOpenRight?: number;
  /**
   * Horizontal distance (in points) required to deactivate
   * the right offset and return to the closed position.
   *
   * If the swipe distance is less than this threshold,
   * the component will snap back to 0.
   * @default 30
   */
  thresholdCloseRight?: number;
  /**
   * Horizontal distance (in points) required to activate
   * the "open" state when swiping to the left.
   *
   * Once this threshold is reached, the component will
   * animate to maxLeftOffset.
   * @default 30
   */
  thresholdOpenLeft?: number;
  /**
   * Horizontal distance (in points) required to deactivate
   * the left offset and return to the closed position.
   *
   * If the swipe distance is less than this threshold,
   * the component will snap back to 0.
   * @default 30
   */
  thresholdCloseLeft?: number;
  /**
   * Called when the swipe gesture becomes active
   * (pan enters active state).
   */
  onSwipeableBegan?: () => void;
  /**
   * Called when the swipe interaction finishes and
   * the component settles into its final position
   * (either opened or closed).
   */
  onSwipeableFinish?: () => void;
}

const COMPONENT_NAME = 'RNSwipeableView';

const NativeSwipeableComponent =
  requireNativeComponent<NativeSwipeableProps>(COMPONENT_NAME);
const Commands = UIManager.getViewManagerConfig(COMPONENT_NAME).Commands;

interface SwipeableProps extends NativeSwipeableProps, PropsWithChildren {
  ref?: RefObject<Nullable<SwipeableRef>>;
}

const RNSwipeableComponent = ({children, ref, ...props}: SwipeableProps) => {
  const nativeRef = useRef(null);

  useImperativeHandle(
    ref,
    () => ({
      close() {
        const node = findNodeHandle(nativeRef.current);
        if (!node) return;

        UIManager.dispatchViewManagerCommand(node, Commands.reset, []);
      },
    }),
    [],
  );

  return (
    <NativeSwipeableComponent ref={nativeRef} {...props}>
      {children}
    </NativeSwipeableComponent>
  );
};

export const closeAllSwipeables = () => {
  if (isAndroid) {
    NativeModules.RNSwipeableModule?.resetAll?.();
    return;
  }
  NativeModules.RNSwipeableView?.resetAll?.();
};

export const RNSwipeable = memo(RNSwipeableComponent);
