import { History } from "history";
import { ReactElement, useContext, useMemo } from "react";
import { RouteRecordType } from "../../builder/RouteRecord";
import { getRouteRecordLocation } from "../../builder/RouteRecord/getRouteRecordLocation";
import { HistoryContext } from "../contexts/HistoryContext";
import { RouteContext } from "../contexts/RouteContext";
import { getNavigationBaseLocation } from "../logic/getNavigationBaseLocation";
import { Navigate, NavigateFunction } from "../types/NavigateFunction";

const cache = new WeakMap<History, Navigate>();

/**
 * Returns the navigate function.
 */
export const useNavigate = (): Navigate => {
  const historyContextObject = useContext(HistoryContext);
  const parentRoute = useContext(RouteContext);
  if (historyContextObject === undefined) {
    throw new Error(
      "No history found in the context. Please make sure you have placed RoconRoot above."
    );
  }
  const { history } = historyContextObject;

  const navigate = useMemo<Navigate>(() => {
    // create navigate function.
    const push = (<Match>(
      route: RouteRecordType<ReactElement | null, Match, boolean>,
      match: Match
    ) => {
      const baseLocation = getNavigationBaseLocation(parentRoute, route);
      history.push(
        getRouteRecordLocation(route, (match || {}) as Match, baseLocation)
      );
    }) as NavigateFunction;
    const replace = (<Match>(
      route: RouteRecordType<ReactElement | null, Match, boolean>,
      match: Match
    ) => {
      const baseLocation = getNavigationBaseLocation(parentRoute, route);
      history.replace(
        getRouteRecordLocation(route, (match || {}) as Match, baseLocation)
      );
    }) as NavigateFunction;
    return Object.assign(push, { push, replace });
  }, [history, parentRoute]);

  return navigate;
};