import { useCallback, useMemo } from 'react';
import { Location, matchPath, useLocation, useParams } from 'react-router-dom';
import type { MatrixClient } from 'matrix-js-sdk';
import {
  decodeRouteParam,
  getDirectPath,
  getExplorePath,
  getHomePath,
  getInboxPath,
  getSpacePath,
} from '../pages/pathUtils';
import {
  DIRECT_PATH,
  EXPLORE_PATH,
  HOME_PATH,
  INBOX_PATH,
  SPACE_FEED_PATH,
  SPACE_LOBBY_PATH,
  SPACE_PATH,
  SPACE_ROOM_PATH,
  SPACE_SEARCH_PATH,
} from '../pages/paths';
import { ScreenSize } from './useScreenSize';
import { useNavigateWithTransition } from './useViewTransitions';
import { useMatrixClientOptionally } from './useMatrixClient';
import { useScreenSizeContext } from './useScreenSize';
import { getCanonicalAliasOrRoomId, getCanonicalAliasRoomId, isRoomAlias } from '../utils/matrix';

type BackRouteContext = {
  location: Location;
  mx: MatrixClient;
  selectedSpaceId?: string;
};

const spaceListPath = (mx: MatrixClient, spaceId: string, spaceIdOrAlias: string): string =>
  getSpacePath(getCanonicalAliasOrRoomId(mx, spaceId) ?? spaceIdOrAlias);

export const getBackRouteTarget = ({
  location,
  mx,
  selectedSpaceId,
}: BackRouteContext): string | null => {
  const { pathname } = location;

  const homeMatch = matchPath({ path: HOME_PATH, caseSensitive: true, end: false }, pathname);
  if (homeMatch) {
    const atRoot = matchPath({ path: HOME_PATH, caseSensitive: true, end: true }, pathname);
    if (!atRoot) {
      return getHomePath();
    }
    return null;
  }

  const directMatch = matchPath({ path: DIRECT_PATH, caseSensitive: true, end: false }, pathname);
  if (directMatch) {
    const atRoot = matchPath({ path: DIRECT_PATH, caseSensitive: true, end: true }, pathname);
    if (!atRoot) {
      return getDirectPath();
    }
    return null;
  }

  const spaceMatch = matchPath({ path: SPACE_PATH, caseSensitive: true, end: false }, pathname);
  if (spaceMatch?.params.spaceIdOrAlias) {
    const spaceIdOrAlias = decodeRouteParam(spaceMatch.params.spaceIdOrAlias);
    if (!spaceIdOrAlias) return null;

    const spaceId = selectedSpaceId ?? spaceIdOrAlias;
    const listPath = spaceListPath(mx, spaceId, spaceIdOrAlias);

    const inRoom = matchPath({ path: SPACE_ROOM_PATH, caseSensitive: true, end: false }, pathname);
    if (inRoom) {
      return listPath;
    }

    const atLobby = matchPath({ path: SPACE_LOBBY_PATH, caseSensitive: true, end: false }, pathname);
    if (atLobby) {
      return listPath;
    }

    const atFeed = matchPath({ path: SPACE_FEED_PATH, caseSensitive: true, end: false }, pathname);
    if (atFeed) {
      return listPath;
    }

    const atSearch = matchPath({ path: SPACE_SEARCH_PATH, caseSensitive: true, end: false }, pathname);
    if (atSearch) {
      return listPath;
    }

    return null;
  }

  const exploreMatch = matchPath({ path: EXPLORE_PATH, caseSensitive: true, end: false }, pathname);
  if (exploreMatch) {
    const atRoot = matchPath({ path: EXPLORE_PATH, caseSensitive: true, end: true }, pathname);
    if (!atRoot) {
      return getExplorePath();
    }
    return null;
  }

  const inboxMatch = matchPath({ path: INBOX_PATH, caseSensitive: true, end: false }, pathname);
  if (inboxMatch) {
    const atRoot = matchPath({ path: INBOX_PATH, caseSensitive: true, end: true }, pathname);
    if (!atRoot) {
      return getInboxPath();
    }
    return null;
  }

  return null;
};

export const showBackInPageHeader = (screenSize: ScreenSize): boolean =>
  screenSize !== ScreenSize.Desktop;

export const showBackInTitleBar = (screenSize: ScreenSize, canGoBack: boolean): boolean =>
  screenSize === ScreenSize.Desktop && canGoBack;

export const useBackRoute = (mxOverride?: MatrixClient | null) => {
  const navigate = useNavigateWithTransition();
  const location = useLocation();
  const mxFromContext = useMatrixClientOptionally();
  const mx = mxOverride ?? mxFromContext;
  const screenSize = useScreenSizeContext();
  const { spaceIdOrAlias: rawSpaceIdOrAlias } = useParams();
  const spaceIdOrAlias = decodeRouteParam(rawSpaceIdOrAlias);
  const selectedSpaceId =
    mx && spaceIdOrAlias && isRoomAlias(spaceIdOrAlias)
      ? getCanonicalAliasRoomId(mx, spaceIdOrAlias)
      : spaceIdOrAlias;

  const target = useMemo(() => {
    if (!mx) return null;
    return getBackRouteTarget({ location, mx, selectedSpaceId });
  }, [location, mx, selectedSpaceId]);

  const goBack = useCallback(() => {
    if (target) {
      navigate(target);
    }
  }, [navigate, target]);

  return {
    canGoBack: target !== null,
    goBack,
    showInPageHeader: showBackInPageHeader(screenSize) && target !== null,
    showInTitleBar: showBackInTitleBar(screenSize, target !== null),
  };
};
