import React, { FC, ComponentType } from 'react';
import { useContext, useState, useRef, useEffect, useCallback, timers } from '@eolme/vma-engine';
import type { AnyTimer } from '@eolme/vma-engine';

import { RouterContext } from '../components/RouterContext';
import { RouterProps } from './withRouter';
import { error } from '../utils/report';
import { HistoryEvent } from '../types';

export interface ThrottlingRouterProps extends RouterProps {
  onTransitionEnd: () => void
}

const MAGIC_UPDATE_INTERVAL = 650;

export function withTransitionRouter<T>(Component: ComponentType<ThrottlingRouterProps & T>): ComponentType<T> {
  const withTransitionRouter: FC<T> = (props: T) => {
    /* eslint-disable react-hooks/rules-of-hooks */
    const router = useContext(RouterContext);

    if (!router) {
      error('Missing router context!');
      return null;
    }

    const lastUpdateRouteAt = useRef(0);
    const updateTimer = useRef<ReturnType<AnyTimer>>(0 as any);
    const updateCallback = useRef<(() => void) | null>(null);

    const [route, setRoute] = useState(router.history.route);
    useEffect(() => {
      const fn = (event: HistoryEvent) => {
        const diff = Date.now() - lastUpdateRouteAt.current;
        if (diff >= MAGIC_UPDATE_INTERVAL) {
          lastUpdateRouteAt.current = Date.now();
          setRoute(event.next);
        } else {
          timers.clearTimeout(updateTimer.current);

          updateCallback.current = () => {
            lastUpdateRouteAt.current = Date.now();
            setRoute(event.next);
            updateCallback.current = null;
          };

          updateTimer.current = timers.setTimeout(updateCallback.current, MAGIC_UPDATE_INTERVAL - diff);
        }
      };

      router.history.on('update', fn);
      return () => {
        router.history.off('update', fn);
      };
    }, [router]);

    const onTransitionEnd = useCallback(() => {
      lastUpdateRouteAt.current = 0;
    }, []);

    return (
      <Component {...props} onTransitionEnd={onTransitionEnd} route={route} />
    );
  };

  const displayName = Component.displayName || Component.name || 'Component';
  withTransitionRouter.displayName = `withTransitionRouter(${displayName})`;

  return withTransitionRouter;
}
