/**
 * TVerticalPagenator
 * checking data-wheel-point and data-spotlight-id
 * @module TVerticalPagenator
 */

import React, {
  forwardRef,
  useCallback,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
} from "react";

import classNames from "classnames";
import { useSelector } from "react-redux";

import Spotlight from "@enact/spotlight";

import usePrevious from "../../hooks/usePrevious";
import * as HelperMethods from "../../utils/helperMethods";
import TScroller from "../TScroller/TScroller";
import css from "./TVerticalPagenator.module.less";

const DEBOUNCE_DELAY = 250;
const DEBUG = false;
const TVerticalPagenator = forwardRef(
  (
    {
      className,
      spotlightId,
      defaultContainerId = null,
      enableFocusAction = true,
      children,
      disabled,
      onScroll,
      onScrollStop,
      onScrollStatusChanged,
      onFocusedContainerId,
      scrollbarVisible = "hidden",
      cbChangePageRef,
      topMargin = 0,
      deleteFocus = false,
      ...rest
    },
    ref
  ) => {
    const [_defaultContainerId, _] = useState(defaultContainerId);
    const pagenatorRef = useRef(null);
    const isScrolling = useRef(false);
    const scrollTopPositionRef = useRef(-1);
    const scrollTo = useRef(null);
    const cursorVisible = useSelector(
      (state) => state.common.appStatus.cursorVisible
    );
    const cursorVisibleRef = usePrevious(cursorVisible);
    const disabledRef = usePrevious(disabled);
    const topMarginRef = usePrevious(topMargin);
    const [initialRendered, setInitialRendered] = useState(false);
    const initialRenderedRef = usePrevious(initialRendered);
    const onFocusedContainerIdRef = usePrevious(onFocusedContainerId);
    const scrollStatus = useRef("top");
    const lastEventTimeRef = useRef(0);

    useImperativeHandle(ref, () => ({
      moveToNode,
      findNextWheelPoint,
    }));

    useEffect(() => {
      if (spotlightId) {
        pagenatorRef.current = document.querySelector(
          `[data-spotlight-id="${spotlightId}"]`
        );
      }
    }, [spotlightId]);

    const findCurrentWheelPoint = useCallback((node) => {
      if (pagenatorRef.current && pagenatorRef.current.contains(node)) {
        return node.closest('[data-wheel-point="true"]');
      } else {
        return null;
      }
    }, []);
    const findNearestWheelPoint = useCallback((next) => {
      if (pagenatorRef.current) {
        const nodes = pagenatorRef.current.querySelectorAll(
          `[data-wheel-point="true"]`
        );
        let nextNode = null,
          prevNode = null,
          minGap = 9999,
          maxGap = -9999;
        for (let i = 0; i < nodes.length; i++) {
          const gap =
            nodes[i].offsetTop -
            HelperMethods.scaleH(topMarginRef.current) -
            scrollTopPositionRef.current;
          if (DEBUG) {
            console.log(
              "TVerticalPagenator findNearestWheelPoint gap, node ",
              gap,
              nodes[i]
            );
          }
          if (gap > 0 && gap < minGap) {
            nextNode = nodes[i];
            minGap = gap;
          }
          if (gap < 0 && gap > maxGap) {
            prevNode = nodes[i];
            maxGap = gap;
          }
        }
        return next ? nextNode : prevNode;
      }
      return null;
    }, []);
    const findNextWheelPoint = useCallback((ev) => {
      return findNearestWheelPoint(true);
    }, []);
    const findPrevWheelPoint = useCallback((ev) => {
      return findNearestWheelPoint(false);
    }, []);
    const onFocusItem = useCallback(
      (ev) => {
        if (!pagenatorRef.current) {
          return;
        }
        ev.stopPropagation();
        ev.preventDefault();
        if (cursorVisibleRef.current) {
          return;
        }
        const node = findCurrentWheelPoint(ev.target);
        if (node) {
          setTimeout(() => {
            moveToNode(node, true, false, true);
          }, 0);
        }
      },
      [moveToNode]
    );

    const handleWheel = useCallback(
      (ev) => {
        const now = Date.now();
        if (now - lastEventTimeRef.current < DEBOUNCE_DELAY) {
          //일정시간 내의 이벤트는 무시
          return;
        }
        lastEventTimeRef.current = now;
        if (DEBUG) {
          console.log("TVerticalPagenator handleWheel ev.deltaY ", ev.deltaY);
        }
        if (pagenatorRef.current.contains(ev.target)) {
          ev.stopPropagation();
          if (disabledRef.current) {
            return;
          }

          if (deleteFocus) {
            const currentSpot = Spotlight.getCurrent();

            if (currentSpot) {
              currentSpot.blur();
            }
          }

          const node =
            ev.deltaY > 0 ? findNextWheelPoint() : findPrevWheelPoint();
          if (node) {
            setTimeout(() => {
              moveToNode(node, true, true);
            }, 0);
          }
        }
      },
      [deleteFocus]
    );

    const getScrollTo = useCallback((cbScrollTo) => {
      scrollTo.current = cbScrollTo;
    }, []);

    useEffect(() => {
      if (pagenatorRef.current) {
        pagenatorRef.current.addEventListener("wheel", handleWheel);
      }
      return () => {
        scrollTo.current = null;
        if (pagenatorRef.current) {
          pagenatorRef.current.removeEventListener("wheel", handleWheel);
        }
      };
    }, []);

    const moveToNode = useCallback(
      (node, animate = true, focus = enableFocusAction, force = false) => {
        if (typeof node === "string") {
          node = pagenatorRef.current.querySelector(
            `[data-spotlight-id="${node}"]`
          );
        }
        if (node) {
          const tMargin = HelperMethods.scaleH(topMarginRef.current);
          const distance = Math.round(node.offsetTop - tMargin);
          let targetTop = distance >= 0 ? distance : 0;
          const currentSpot = Spotlight.getCurrent();
          if (DEBUG) {
            console.log("TVerticalPagenator moveToNode node ", node, targetTop);
          }
          if (!force && scrollTopPositionRef.current === targetTop) {
            if (DEBUG) {
              console.log(
                "TVerticalPagenator moveToNode node ignore same targetTop"
              );
            }
            return;
          }
          scrollTopPositionRef.current = targetTop;
          scrollTo.current &&
            scrollTo.current({
              position: { x: 0, y: targetTop },
              animate: initialRenderedRef.current && animate,
              focus: focus || !currentSpot,
              align: "top",
            });
          if (onFocusedContainerIdRef.current) {
            if (!node.getAttribute("data-spotlight-id")) {
              console.warn("TVerticalPagenator should have spotlight id");
            }
            onFocusedContainerIdRef.current(
              node.getAttribute("data-spotlight-id")
            );
          }
        }
      },
      [enableFocusAction]
    );

    useEffect(() => {
      let node = _defaultContainerId;
      // console.log('yhcho TVerticalPagenator _defaultContainerId changed ', _defaultContainerId);
      if (!_defaultContainerId) {
        node = pagenatorRef.current.querySelector(`[data-wheel-point="true"]`); //to top
      }
      if (!initialRenderedRef.current) {
        // moveToNode(node, false);

        setTimeout(() => {
          moveToNode(node, false);
          setInitialRendered(true);
        }, 100);
      } else {
        moveToNode(node, true);
      }
    }, [_defaultContainerId]);

    useEffect(() => {
      if (cbChangePageRef) {
        cbChangePageRef.current = (_spotlightId, animate = false, focus) => {
          let node = null;
          if (!_spotlightId) {
            //to top
            node = pagenatorRef.current.querySelector(
              `[data-wheel-point="true"]`
            );
          } else {
            node = pagenatorRef.current.querySelector(
              `[data-spotlight-id="${_spotlightId}"]`
            );
          }
          if (node) {
            setTimeout(() => {
              moveToNode(node, animate, focus);
            }, 0);
          }
        };
      }
    }, [cbChangePageRef]);

    const _onScrollStart = useCallback(() => {
      isScrolling.current = true;
    }, []);

    const _onScrollStop = useCallback(
      (ev) => {
        isScrolling.current = false;
        scrollTopPositionRef.current = ev.scrollTop;
        if (onScrollStop) {
          onScrollStop(ev);
        }
      },
      [onScrollStop]
    );

    const _onScroll = useCallback(
      (ev) => {
        isScrolling.current = true;
        let prevScrollStatus = scrollStatus.current;
        const scroller = pagenatorRef.current?.childNodes?.[0];
        if (scroller) {
          if (ev.scrollTop <= 0) {
            scrollStatus.current = "top";
          } else if (
            ev.scrollTop + scroller.offsetHeight >=
            scroller.scrollHeight
          ) {
            scrollStatus.current = "end";
          } else {
            scrollStatus.current = "middle";
          }
        }
        if (
          prevScrollStatus !== scrollStatus.current &&
          onScrollStatusChanged
        ) {
          onScrollStatusChanged(scrollStatus.current);
        }
        if (onScroll) {
          onScroll(ev);
        }
      },
      [onScrollStatusChanged, onScroll]
    );

    if (!spotlightId) {
      console.warn("TVerticalPagenator has no spotlightId");
      return null;
    }
    return (
      <TScroller
        {...rest}
        id={spotlightId}
        spotlightId={spotlightId}
        className={classNames(
          css.verticalPagenator,
          initialRendered && css.show,
          className
        )}
        focusableScrollbar={scrollbarVisible !== "hidden"}
        noScrollByWheel={true}
        noScrollByDrag={true}
        verticalScrollbar={scrollbarVisible}
        onFocus={onFocusItem}
        cbScrollTo={getScrollTo}
        onScroll={_onScroll}
        onScrollStart={_onScrollStart}
        onScrollStop={_onScrollStop}
      >
        {children}
      </TScroller>
    );
  }
);

export default TVerticalPagenator;
