/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { forwardRef, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import clsx from 'clsx';
import useRefs from '../hooks/use-refs';
import { getRect } from '../hooks/use-rect';
import useTouch from '../hooks/use-touch';
import useScrollParent from '../hooks/use-scroll-parent';
import useEventListener from '../hooks/use-event-listener';
import IndexBarContext from './IndexBarContext';
import PopupContext from '../popup/PopupContext';
import { isDef, isHidden, getScrollTop, preventDefault, setRootScrollTop, getRootScrollTop, setScrollTop } from '../utils';
import { useMount } from '../hooks';
import { renderToContainer } from '../utils/dom/renderToContainer';
import useSsrCompat from '../hooks/use-ssr-compat';
import { INDEX_ANCHORE_KEY } from './IndexAnchor';
import ConfigProviderContext from '../config-provider/ConfigProviderContext';
import { COMPONENT_TYPE_KEY } from '../utils/constant';
var IndexBar = /*#__PURE__*/forwardRef(function (props, ref) {
  var popupContext = useContext(PopupContext);

  var _useContext = useContext(ConfigProviderContext),
      prefixCls = _useContext.prefixCls,
      createNamespace = _useContext.createNamespace;

  var _createNamespace = createNamespace('index-bar', prefixCls),
      bem = _createNamespace[0];

  var children = props.children,
      sticky = props.sticky,
      zIndex = props.zIndex,
      highlightColor = props.highlightColor;

  var _useState = useState(null),
      activeAnchor = _useState[0],
      setActiveAnchor = _useState[1];

  var root = useRef(null);
  var sidebar = useRef(null);
  var touchActiveIndex = useRef(null);
  var touch = useTouch();
  var scrollParent = useScrollParent(root);

  var _useRefs = useRefs(),
      refs = _useRefs[0],
      setRefs = _useRefs[1];

  var _useSsrCompat = useSsrCompat(),
      ssrCompatRender = _useSsrCompat[0];

  var sidebarStyle = useMemo(function () {
    if (isDef(zIndex)) {
      return {
        zIndex: +zIndex + 1
      };
    }

    return {};
  }, [zIndex]);
  var highlightStyle = useMemo(function () {
    if (highlightColor) {
      return {
        color: highlightColor
      };
    }

    return {};
  }, [highlightColor]);

  var getScrollerRect = function getScrollerRect() {
    if (scrollParent.getBoundingClientRect) {
      return getRect(scrollParent);
    }

    return {
      top: 0,
      left: 0
    };
  };

  var getActiveAnchor = function getActiveAnchor(scrollTop, rects) {
    for (var i = rects.length - 1; i >= 0; i -= 1) {
      var prevHeight = i > 0 ? rects[i - 1].height : 0;
      var reachTop = props.sticky ? prevHeight + props.stickyOffsetTop : 0;

      if (scrollTop + reachTop >= rects[i].top) {
        return i;
      }
    }

    return -1;
  };

  var onScroll = function onScroll() {
    if (isHidden(root.current) || !scrollParent) {
      return;
    }

    var indexList = props.indexList;
    var scrollTop = getScrollTop(scrollParent);
    var scrollParentRect = getScrollerRect();
    var rects = Object.values(refs).map(function (anchor) {
      return anchor.getRect(scrollParent, scrollParentRect);
    });
    var active = getActiveAnchor(scrollTop, rects);
    setActiveAnchor(indexList[active]);

    if (sticky) {
      Object.values(refs).forEach(function (item, index) {
        var updateState = item.updateState;

        if (index === active || index === active - 1) {
          var rect = item.root.current.getBoundingClientRect();
          updateState({
            left: rect.left,
            width: rect.width
          });
        } else {
          updateState({
            left: null,
            width: null
          });
        }

        if (index === active) {
          updateState({
            active: true,
            top: Math.max(props.stickyOffsetTop, rects[index].top - scrollTop) + scrollParentRect.top
          });
        } else if (index === active - 1) {
          var activeItemTop = rects[active].top - scrollTop;
          updateState({
            active: activeItemTop > 0,
            top: activeItemTop + scrollParentRect.top - rects[index].height
          });
        } else {
          updateState({
            active: false
          });
        }
      });
    }
  };

  var init = function init() {
    return setTimeout(onScroll, 0);
  };

  var renderIndexes = function renderIndexes() {
    return props.indexList.map(function (index) {
      var active = index === activeAnchor;
      return /*#__PURE__*/React.createElement("span", {
        key: index,
        className: clsx(bem('index', {
          active: active
        })),
        style: active ? highlightStyle : null,
        "data-index": index
      }, index);
    });
  };

  var onTouchStart = function onTouchStart(event) {
    touch.start(event);
  };

  var scrollTo = function scrollTo(index) {
    if (!index) {
      return;
    }

    if (refs[index]) {
      refs[index].root.current.scrollIntoView();

      if (props.sticky && props.stickyOffsetTop) {
        if (popupContext.visible) {
          setScrollTop(scrollParent, getScrollTop(scrollParent) - props.stickyOffsetTop);
        } else {
          setRootScrollTop(getRootScrollTop() - props.stickyOffsetTop);
        }
      }

      if (props.onSelect && typeof props.onSelect === 'function') {
        props.onSelect(activeAnchor);
      }
    }
  };

  var scrollToElement = function scrollToElement(element) {
    var index = element.dataset.index;
    scrollTo(index);
  };

  var onClickSidebar = function onClickSidebar(event) {
    scrollToElement(event.target);
  };

  var onTouchMove = function onTouchMove(event) {
    touch.move(event);
    preventDefault(event);

    if (touch.isVertical()) {
      var _event$touches$ = event.touches[0],
          clientX = _event$touches$.clientX,
          clientY = _event$touches$.clientY;
      var target = document.elementFromPoint(clientX, clientY);

      if (target) {
        var index = target.dataset.index;
        /* istanbul ignore else */

        if (touchActiveIndex.current !== index) {
          touchActiveIndex.current = index;
          scrollToElement(target);
        }
      }
    }
  };

  useEventListener('scroll', onScroll, {
    target: scrollParent,
    depends: [scrollParent]
  });
  useEventListener('touchmove', onTouchMove, {
    target: sidebar.current,
    depends: [touch.deltaY]
  });
  useEffect(function () {
    if (props.onChange && typeof props.onChange === 'function') {
      props.onChange(activeAnchor);
    }
  }, [activeAnchor]);
  useMount(init);

  var handleMapChildren = function handleMapChildren($children) {
    return React.Children.toArray($children).filter(Boolean).map(function (child) {
      var _child$type, _child$props;

      if (((_child$type = child.type) == null ? void 0 : _child$type[COMPONENT_TYPE_KEY]) === INDEX_ANCHORE_KEY) {
        return /*#__PURE__*/React.cloneElement(child, {
          ref: setRefs(child.props.index)
        });
      }

      if ((_child$props = child.props) != null && _child$props.children) {
        var deepMap = handleMapChildren(child.props.children);
        return deepMap.length ? deepMap : child;
      }

      return child;
    });
  };

  useImperativeHandle(ref, function () {
    return {
      scrollTo: scrollTo
    };
  });
  var memoChildren = useMemo(function () {
    return handleMapChildren(children);
  }, [children]);
  return /*#__PURE__*/React.createElement(IndexBarContext.Provider, {
    value: {
      zIndex: zIndex,
      highlightColor: highlightColor,
      sticky: sticky
    }
  }, /*#__PURE__*/React.createElement("div", {
    ref: root,
    className: clsx(bem(), props.className),
    style: props.style
  }, ssrCompatRender(function () {
    return renderToContainer(props.teleport, /*#__PURE__*/React.createElement("div", {
      ref: sidebar,
      className: clsx(bem('sidebar')),
      style: sidebarStyle,
      onClick: onClickSidebar,
      onTouchStart: onTouchStart
    }, renderIndexes()));
  }), memoChildren));
});

function genAlphabet() {
  var indexList = [];
  var charCodeOfA = 'A'.charCodeAt(0);

  for (var i = 0; i < 26; i += 1) {
    indexList.push(String.fromCharCode(charCodeOfA + i));
  }

  return indexList;
}

IndexBar.defaultProps = {
  sticky: true,
  stickyOffsetTop: 0,
  indexList: genAlphabet()
};
export default IndexBar;