import { createContext, useContext, useEffect, useMemo } from 'react';
import { useRouter as useNextRouter } from 'next/router';

import type { FC, ReactNode } from 'react';

import { useSelector } from '~/src/store/redux';
import {
  selectAccountIdSessionScope,
  selectPathSessionScope,
  selectSessionScope,
} from '~/src/store/session/selectors';
import { AppRouter } from './AppRouter';

declare global {
  interface Window {
    __router: AppRouter;
  }
}

const AppRouterContext = createContext<{ router: AppRouter }>({} as any);

export const useAppRouter = () => useContext(AppRouterContext).router;

export const AppRouterProvider: FC<{ children: ReactNode }> = ({
  children,
}) => {
  const accountIdScope = useSelector(selectAccountIdSessionScope);
  const pathScope = useSelector(selectPathSessionScope);
  const scope = useSelector(selectSessionScope);

  // this is a new object each time the route changes
  const nextRouter = useNextRouter();

  // router singleton
  const router = useMemo(
    () =>
      new AppRouter({
        nextRouter,
        accountIdScope,
        pathScope,
        scope,
      }),
    []
  );

  useEffect(() => {
    return () => {
      router.destroy();
    };
  }, []);

  if (typeof window !== 'undefined') {
    window.__router = router;
  }

  // keep the underlying nextRouter reference up to date
  // so dependent components always get the latest state
  router.setNextRouter(nextRouter);

  // force the value object to change whenever the `asPath` or `isReady` changes,
  // this ensure dependent components render whenever the url changes or router is ready
  const value = useMemo(
    () => ({
      router,
    }),
    [nextRouter.asPath]
  );

  return (
    <AppRouterContext.Provider value={value}>
      {children}
    </AppRouterContext.Provider>
  );
};
