import { jsx as _jsx } from "react/jsx-runtime";
import { useRef, useMemo, useContext } from 'react';
import clsx from 'clsx';
import useScrollParent from '../hooks/use-scroll-parent';
import useEventListener from '../hooks/use-event-listener';
import { getScrollTop, unitToPx, isHidden, getZIndexStyle, extend } from '../utils';
import { useSetState, useUpdateEffect, useVisibilityChange } from '../hooks';
import { getRect } from '../hooks/use-rect';
import ConfigProviderContext from '../config-provider/ConfigProviderContext';

const Sticky = props => {
  const {
    prefixCls,
    createNamespace
  } = useContext(ConfigProviderContext);
  const [bem] = createNamespace('sticky', prefixCls);
  const [state, updateState] = useSetState({
    fixed: false,
    width: 0,
    height: 0,
    transform: 0
  });
  const root = useRef(null);
  const scrollParent = useScrollParent(root);
  const offset = useMemo(() => unitToPx(props.position === 'top' ? props.offsetTop : props.offsetBottom), [props.position, props.offsetTop, props.offsetBottom]);
  const rootStyle = useMemo(() => {
    const {
      fixed,
      height,
      width
    } = state;

    if (fixed) {
      return {
        width: `${width}px`,
        height: `${height}px`
      };
    }

    return null;
  }, [state.fixed, state.height, state.width]);
  const stickyStyle = useMemo(() => {
    if (!state.fixed) {
      return null;
    }

    const style = extend(getZIndexStyle(props.zIndex), {
      width: `${state.width}px`,
      height: `${state.height}px`,
      [props.position]: `${offset}px`
    });

    if (state.transform) {
      style.transform = `translate3d(0, ${state.transform}px, 0)`;
    }

    return style;
  }, [props.position, state.fixed, offset, state.width, state.height, state.transform]);

  const emitScroll = (scrollTop, isFixed) => {
    if (props.onScroll) {
      props.onScroll({
        scrollTop,
        isFixed
      });
    }
  };

  const onScroll = () => {
    if (!root.current || isHidden(root.current)) {
      return;
    }

    const {
      container,
      position
    } = props;
    const rootRect = getRect(root.current);
    const scrollTop = getScrollTop(window);
    const newState = {};
    newState.width = rootRect.width;
    newState.height = rootRect.height;

    if (position === 'top') {
      // The sticky component should be kept inside the container element
      if (container) {
        const containerRect = getRect(container.current);
        const difference = containerRect.bottom - offset - newState.height;
        newState.fixed = offset > rootRect.top && containerRect.bottom > 0;
        newState.transform = difference < 0 ? difference : 0;
      } else {
        newState.fixed = offset > rootRect.top;
      }
    } else {
      const {
        clientHeight
      } = document.documentElement;

      if (container) {
        const containerRect = getRect(container.current);
        const difference = clientHeight - containerRect.top - offset - newState.height;
        newState.fixed = clientHeight - offset < rootRect.bottom && clientHeight > containerRect.top;
        newState.transform = difference < 0 ? -difference : 0;
      } else {
        newState.fixed = clientHeight - offset < rootRect.bottom;
      }
    }

    updateState(newState);
    emitScroll(scrollTop, newState.fixed);
  };

  useEventListener('scroll', onScroll, {
    target: scrollParent
  });
  useVisibilityChange(root, onScroll);
  useUpdateEffect(() => {
    var _a;

    (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, state.fixed);
  }, [state.fixed]);
  return _jsx("div", Object.assign({
    ref: root,
    style: rootStyle
  }, {
    children: _jsx("div", Object.assign({
      className: clsx(bem({
        fixed: state.fixed
      })),
      style: stickyStyle
    }, {
      children: props.children
    }))
  }));
};

Sticky.defaultProps = {
  offsetTop: 0,
  offsetBottom: 0,
  position: 'top'
};
export default Sticky;