import { useCallback } from 'react';
import { NavigateOptions, useLocation, useNavigate } from 'react-router-dom';
import { useAtomValue } from 'jotai';
import { getCanonicalAliasOrRoomId } from '../utils/matrix';
import {
  getDirectRoomPath,
  getHomeRoomPath,
  getSpacePath,
  getSpaceRoomPath,
  withRoomEventId,
} from '../pages/pathUtils';
import { useMatrixClient } from './useMatrixClient';
import { getOrphanParents, guessPerfectParent, isSpace } from '../utils/room';
import { roomToParentsAtom } from '../state/room/roomToParents';
import { mDirectAtom } from '../state/mDirectList';
import { useSelectedRoom } from './router/useSelectedRoom';
import { useSelectedSpace } from './router/useSelectedSpace';
import { settingsAtom } from '../state/settings';
import { useSetting } from '../state/hooks/settings';

export const useRoomNavigate = () => {
  const navigate = useNavigate();
  const location = useLocation();
  const mx = useMatrixClient();
  const roomToParents = useAtomValue(roomToParentsAtom);
  const mDirects = useAtomValue(mDirectAtom);
  const spaceSelectedId = useSelectedSpace();
  const selectedRoomId = useSelectedRoom();
  const [developerTools] = useSetting(settingsAtom, 'developerTools');

  const navigateSpace = useCallback(
    (roomId: string) => {
      const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, roomId);
      navigate(getSpacePath(roomIdOrAlias));
    },
    [mx, navigate]
  );

  const navigateRoom = useCallback(
    (roomId: string, eventId?: string, opts?: NavigateOptions) => {
      // Already viewing this room — keep the current home/direct/space URL and only
      // change the event id. Otherwise guessPerfectParent can yank the sidebar to
      // wherever the room "belongs".
      if (selectedRoomId === roomId) {
        navigate(withRoomEventId(location.pathname, eventId), opts);
        return;
      }

      const room = mx.getRoom(roomId);
      const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, roomId);
      const openSpaceTimeline = developerTools && spaceSelectedId === roomId;

      if (room && isSpace(room) && !openSpaceTimeline) {
        navigate(getSpacePath(roomIdOrAlias), opts);
        return;
      }

      const orphanParents = openSpaceTimeline ? [roomId] : getOrphanParents(roomToParents, roomId);

      // Prefer Direct for m.direct rooms unless we are already browsing a parent space
      // of this room (keep space chrome). Opening via space while roomToParents is still
      // catching up used to hit JoinBeforeNavigate even though the DM was listed.
      if (mDirects.has(roomId)) {
        const stayInSelectedSpace =
          Boolean(spaceSelectedId) && orphanParents.includes(spaceSelectedId!);
        if (!stayInSelectedSpace) {
          navigate(getDirectRoomPath(roomIdOrAlias, eventId), opts);
          return;
        }
      }

      if (orphanParents.length > 0) {
        let parentSpace: string;
        if (spaceSelectedId && orphanParents.includes(spaceSelectedId)) {
          parentSpace = spaceSelectedId;
        } else {
          parentSpace = guessPerfectParent(mx, roomId, orphanParents) ?? orphanParents[0];
        }

        const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(mx, parentSpace);

        navigate(
          getSpaceRoomPath(pSpaceIdOrAlias, openSpaceTimeline ? roomId : roomIdOrAlias, eventId),
          opts
        );
        return;
      }

      navigate(getHomeRoomPath(roomIdOrAlias, eventId), opts);
    },
    [
      mx,
      navigate,
      location.pathname,
      selectedRoomId,
      spaceSelectedId,
      roomToParents,
      mDirects,
      developerTools,
    ]
  );

  return {
    navigateSpace,
    navigateRoom,
  };
};
