import React, {
  MouseEventHandler,
  forwardRef,
  useCallback,
  useMemo,
  useRef,
  useState,
} from 'react';
import { useAtom, useAtomValue } from 'jotai';
import { Avatar, Box, Button, IconButton, Line, Menu, MenuItem, PopOut, RectCords, Spinner, Text, color, config, toRem } from 'folds';
import { Icon, Icons } from '../../../components/icons';
import { JoinRule, Room, Thread, ThreadEvent } from 'matrix-js-sdk';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
import FocusTrap from 'focus-trap-react';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { mDirectAtom } from '../../../state/mDirectList';
import {
  NavCategory,
  NavCategoryHeader,
  NavItem,
  NavItemContent,
  NavLink,
} from '../../../components/nav';
import { getSpaceLobbyPath, getSpaceRoomPath, getSpaceSearchPath } from '../../pathUtils';
import { getCanonicalAliasOrRoomId, isRoomAlias } from '../../../utils/matrix';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import {
  useSpaceLobbySelected,
  useSpaceSearchSelected,
} from '../../../hooks/router/useSelectedSpace';
import { useSpace } from '../../../hooks/useSpace';
import { RoomNavCategoryButton, RoomNavItem, UnjoinedSubRoomItem } from '../../../features/room-nav';
import { makeNavCategoryId } from '../../../state/closedNavCategories';
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
import { useRoomName } from '../../../hooks/useRoomMeta';
import { useSpaceJoinedHierarchy } from '../../../hooks/useSpaceHierarchy';
import { allRoomsAtom } from '../../../state/room-list/roomList';
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
import { usePowerLevels } from '../../../hooks/usePowerLevels';
import { useRecursiveChildScopeFactory, useSpaceChildren } from '../../../state/hooks/roomList';
import { roomToParentsAtom } from '../../../state/room/roomToParents';
import { useMarkRoomsAsRead } from '../../../hooks/useMarkAsRead';
import { useRoomsUnread } from '../../../state/hooks/unread';
import { UseStateProvider } from '../../../components/UseStateProvider';
import { LeaveSpacePrompt } from '../../../components/leave-space-prompt';
import { copyToClipboard } from '../../../utils/dom';
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
import { useStateEvent } from '../../../hooks/useStateEvent';
import { Membership, StateEvent } from '../../../../types/matrix/room';
import { stopPropagation } from '../../../utils/keyboard';
import { PluginNavSlot } from '../../../features/settings/plugins/PluginNavSlot';
import { getMatrixToRoom } from '../../../plugins/matrix-to';
import { getViaServers } from '../../../plugins/via-servers';
import { useSetting } from '../../../state/hooks/settings';
import { settingsAtom } from '../../../state/settings';
import {
  getRoomNotificationMode,
  useRoomsNotificationPreferencesContext,
} from '../../../hooks/useRoomsNotificationPreferences';
import { useOpenSpaceSettings } from '../../../state/hooks/spaceSettings';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
import { useRoomCreators } from '../../../hooks/useRoomCreators';
import { useRoomPermissions } from '../../../hooks/useRoomPermissions';
import { ContainerColor } from '../../../styles/ContainerColor.css';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { BreakWord } from '../../../styles/Text.css';
import { InviteUserPrompt } from '../../../components/invite-user-prompt';
import { isSpace, shouldShowForumLobby } from '../../../utils/room';
import { SpaceOptionsMenu } from '../../../features/space/SpaceOptionsMenu';
import { ForumFeedSidebar } from '../../../features/forum/ForumFeedSidebar';
import * as forumBoardCss from '../../../features/forum/ForumBoardView.css';

type SpaceHierarchyItem =
  | { type: 'hierarchy'; roomId: string }
  | { type: 'subroom'; roomId: string; room: Room; depth: number; isLast?: boolean }
  | { type: 'unjoined-subroom'; roomId: string; depth: number; isLast?: boolean };

type SpaceNavSectionData = {
  spaceRoomId: string;
  categoryId: string;
  label: string;
  items: SpaceHierarchyItem[];
};

type SpaceNavSectionProps = {
  section: SpaceNavSectionData;
  closed: boolean;
  onCategoryClick: MouseEventHandler<HTMLButtonElement>;
  selectedRoomId?: string;
  mDirects: Set<string>;
  notificationPreferences: ReturnType<typeof useRoomsNotificationPreferencesContext>;
  getToLink: (roomId: string) => string;
};

function SpaceNavSection({
  section,
  closed,
  onCategoryClick,
  selectedRoomId,
  mDirects,
  notificationPreferences,
  getToLink,
}: SpaceNavSectionProps) {
  const mx = useMatrixClient();

  return (
    <NavCategory data-nav-dropdown="">
      <NavCategoryHeader>
        <RoomNavCategoryButton
          data-category-id={section.categoryId}
          onClick={onCategoryClick}
          closed={closed}
        >
          {section.label}
        </RoomNavCategoryButton>
      </NavCategoryHeader>
      {section.items.length > 0 && (
        <div data-nav-rooms="">
          <div data-nav-room-list="">
            {section.items.map((item, index) => {
              if (item.type === 'unjoined-subroom') {
                return (
                  <UnjoinedSubRoomItem
                    key={`${item.roomId}:${index}`}
                    roomId={item.roomId}
                    depth={item.depth}
                    isLast={item.isLast}
                  />
                );
              }

              if (item.type === 'subroom') {
                return (
                  <div
                    key={`${item.roomId}:${index}`}
                    data-nav-depth={item.depth}
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      position: 'relative',
                    }}
                  >
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <RoomNavItem
                        room={item.room}
                        selected={selectedRoomId === item.roomId}
                        hideIcon={item.depth > 0}
                        showAvatar={mDirects.has(item.roomId)}
                        direct={mDirects.has(item.roomId)}
                        linkPath={getToLink(item.roomId)}
                        notificationMode={getRoomNotificationMode(
                          notificationPreferences,
                          item.roomId
                        )}
                      />
                    </div>
                  </div>
                );
              }

              const room = mx.getRoom(item.roomId);
              if (!room || isSpace(room)) return null;

              return (
                <RoomNavItem
                  key={`${item.roomId}:${index}`}
                  room={room}
                  selected={selectedRoomId === item.roomId}
                  showAvatar={mDirects.has(item.roomId)}
                  direct={mDirects.has(item.roomId)}
                  linkPath={getToLink(item.roomId)}
                  notificationMode={getRoomNotificationMode(
                    notificationPreferences,
                    room.roomId
                  )}
                />
              );
            })}
          </div>
        </div>
      )}
    </NavCategory>
  );
}

function SpaceHeader() {
  const space = useSpace();
  const spaceName = useRoomName(space);
  const [menuAnchor, setMenuAnchor] = useState<RectCords>();

  const joinRules = useStateEvent(
    space,
    StateEvent.RoomJoinRules
  )?.getContent<RoomJoinRulesEventContent>();

  const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    const cords = evt.currentTarget.getBoundingClientRect();
    setMenuAnchor((currentState) => {
      if (currentState) return undefined;
      return cords;
    });
  };

  return (
    <>
      <PageNavHeader>
        <Box alignItems="Center" grow="Yes" gap="300">
          <Box grow="Yes" alignItems="Center" gap="100">
            <Text size="H4" truncate>
              {spaceName}
            </Text>
            {joinRules?.join_rule !== JoinRule.Public && <Icon src={Icons.Lock} size="50" />}
          </Box>
          <Box shrink="No">
            <IconButton aria-pressed={!!menuAnchor} variant="Background" onClick={handleOpenMenu}>
              <Icon src={Icons.VerticalDots} size="200" />
            </IconButton>
          </Box>
        </Box>
      </PageNavHeader>
      {menuAnchor && (
        <PopOut
          anchor={menuAnchor}
          position="Bottom"
          align="End"
          offset={6}
          content={
            <FocusTrap
              focusTrapOptions={{
                initialFocus: false,
                returnFocusOnDeactivate: false,
                onDeactivate: () => setMenuAnchor(undefined),
                clickOutsideDeactivates: true,
                isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
                isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
                escapeDeactivates: stopPropagation,
              }}
            >
              <SpaceOptionsMenu room={space} requestClose={() => setMenuAnchor(undefined)} />
            </FocusTrap>
          }
        />
      )}
    </>
  );
}

type SpaceTombstoneProps = { roomId: string; replacementRoomId: string };
export function SpaceTombstone({ roomId, replacementRoomId }: SpaceTombstoneProps) {
  const mx = useMatrixClient();
  const { navigateSpace } = useRoomNavigate();

  const [joinState, handleJoin] = useAsyncCallback(
    useCallback(() => {
      const currentRoom = mx.getRoom(roomId);
      const via = currentRoom ? getViaServers(currentRoom) : [];
      return mx.joinRoom(replacementRoomId, {
        viaServers: via,
      });
    }, [mx, roomId, replacementRoomId])
  );
  const replacementRoom = mx.getRoom(replacementRoomId);

  const handleOpen = () => {
    if (replacementRoom) navigateSpace(replacementRoom.roomId);
    if (joinState.status === AsyncStatus.Success) navigateSpace(joinState.data.roomId);
  };

  return (
    <Box
      style={{
        padding: config.space.S200,
        borderRadius: config.radii.R400,
        borderWidth: config.borderWidth.B300,
      }}
      className={ContainerColor({ variant: 'Surface' })}
      direction="Column"
      gap="300"
    >
      <Box direction="Column" grow="Yes" gap="100">
        <Text size="L400">Space Upgraded</Text>
        <Text size="T200">This space has been replaced and is no longer active.</Text>
        {joinState.status === AsyncStatus.Error && (
          <Text className={BreakWord} style={{ color: color.Critical.Main }} size="T200">
            {(joinState.error as any)?.message ?? 'Failed to join replacement space!'}
          </Text>
        )}
      </Box>
      <Box direction="Column" shrink="No">
        {replacementRoom?.getMyMembership() === Membership.Join ||
        joinState.status === AsyncStatus.Success ? (
          <Button onClick={handleOpen} size="300" variant="Success" fill="Solid" radii="300">
            <Text size="B300">Open New Space</Text>
          </Button>
        ) : (
          <Button
            onClick={handleJoin}
            size="300"
            variant="Primary"
            fill="Solid"
            radii="300"
            before={
              joinState.status === AsyncStatus.Loading && (
                <Spinner size="100" variant="Primary" fill="Solid" />
              )
            }
            disabled={joinState.status === AsyncStatus.Loading}
          >
            <Text size="B300">Join New Space</Text>
          </Button>
        )}
      </Box>
    </Box>
  );
}

export function Space() {
  const mx = useMatrixClient();
  const space = useSpace();
  useNavToActivePathMapper(space.roomId);
  const spaceIdOrAlias = getCanonicalAliasOrRoomId(mx, space.roomId);
  const scrollRef = useRef<HTMLDivElement>(null);
  const mDirects = useAtomValue(mDirectAtom);
  const roomToUnread = useAtomValue(roomToUnreadAtom);
  const allRooms = useAtomValue(allRoomsAtom);
  const allJoinedRooms = useMemo(() => new Set(allRooms), [allRooms]);
  const notificationPreferences = useRoomsNotificationPreferencesContext();

  const tombstoneEvent = useStateEvent(space, StateEvent.RoomTombstone);

  const selectedRoomId = useSelectedRoom();
  const lobbySelected = useSpaceLobbySelected(spaceIdOrAlias);
  const searchSelected = useSpaceSearchSelected(spaceIdOrAlias);

  const [closedCategories, setClosedCategories] = useAtom(useClosedNavCategoriesAtom());

  const getRoom = useCallback(
    (rId: string) => {
      if (allJoinedRooms.has(rId)) {
        return mx.getRoom(rId) ?? undefined;
      }
      return undefined;
    },
    [mx, allJoinedRooms]
  );

  const hierarchy = useSpaceJoinedHierarchy(
    space.roomId,
    getRoom,
    useCallback(
      (parentId, roomId) => {
        if (!closedCategories.has(makeNavCategoryId(space.roomId, parentId))) {
          return false;
        }
        const showRoom = roomToUnread.has(roomId) || roomId === selectedRoomId;
        if (showRoom) return false;
        return true;
      },
      [space.roomId, closedCategories, roomToUnread, selectedRoomId]
    ),
    useCallback(
      (sId) => closedCategories.has(makeNavCategoryId(space.roomId, sId)),
      [closedCategories, space.roomId]
    )
  );

  // Build a global set of all sub-room IDs to filter from hierarchy
  // This prevents sub-rooms from appearing both as top-level entries AND nested under parents
  const globalSubRoomIds = useMemo(() => {
    const subRoomIds = new Set<string>();
    mx.getRooms().forEach((room) => {
      const subRoomsEvent = room.currentState.getStateEvents(StateEvent.PaarrotSubRooms, '');
      const content = subRoomsEvent?.getContent<{ children: string[] }>();
      content?.children?.forEach((childId: string) => subRoomIds.add(childId));
    });
    return subRoomIds;
  }, [mx, allJoinedRooms, hierarchy]);

  // Filter hierarchy to exclude sub-rooms (they'll be shown nested under their parent)
  const filteredHierarchy = useMemo(() => {
    return hierarchy.filter((entry) => !globalSubRoomIds.has(entry.roomId));
  }, [hierarchy, globalSubRoomIds]);

  // One paper dropdown per space: space header + its rooms/sub-rooms
  const navSections = useMemo(() => {
    const sections: SpaceNavSectionData[] = [];
    let current: SpaceNavSectionData | null = null;
    const processedSubRooms = new Set<string>();

    const addSubRooms = (parentRoomId: string, baseDepth: number, into: SpaceHierarchyItem[]) => {
      const room = mx.getRoom(parentRoomId);
      if (!room || isSpace(room)) return;

      const subRoomsEvent = room.currentState.getStateEvents(StateEvent.PaarrotSubRooms, '');
      const subRooms = subRoomsEvent?.getContent<{ children: string[] }>()?.children ?? [];

      const joinedSubRooms = subRooms.filter(
        (subRoomId) => !processedSubRooms.has(subRoomId) && mx.getRoom(subRoomId)
      );

      joinedSubRooms.forEach((subRoomId, index) => {
        processedSubRooms.add(subRoomId);
        const isLast = index === joinedSubRooms.length - 1;
        const subRoom = mx.getRoom(subRoomId);
        if (!subRoom) return;
        into.push({
          type: 'subroom',
          roomId: subRoomId,
          room: subRoom,
          depth: baseDepth + 1,
          isLast,
        });
        addSubRooms(subRoomId, baseDepth + 1, into);
      });
    };

    const startSection = (spaceRoomId: string, label: string) => {
      current = {
        spaceRoomId,
        categoryId: makeNavCategoryId(space.roomId, spaceRoomId),
        label,
        items: [],
      };
      sections.push(current);
      return current;
    };

    filteredHierarchy.forEach((entry) => {
      const isSpaceEntry = 'space' in entry && entry.space === true;
      const room = mx.getRoom(entry.roomId);

      if (isSpaceEntry || (room && isSpace(room))) {
        startSection(
          entry.roomId,
          entry.roomId === space.roomId ? 'Rooms' : room?.name ?? entry.roomId
        );
        return;
      }

      if (!current) {
        startSection(space.roomId, 'Rooms');
      }

      current!.items.push({ type: 'hierarchy', roomId: entry.roomId });
      if (room && !isSpace(room)) {
        addSubRooms(entry.roomId, 0, current!.items);
      }
    });

    return sections;
  }, [mx, filteredHierarchy, space.roomId]);

  const handleCategoryClick = useCategoryHandler(setClosedCategories, (categoryId) =>
    closedCategories.has(categoryId)
  );

  const getToLink = (roomId: string) =>
    getSpaceRoomPath(spaceIdOrAlias, getCanonicalAliasOrRoomId(mx, roomId));

  const isForum = shouldShowForumLobby(space);
  // Forum feed unless user opened manage, search, or a room (not tied to URL alias encoding).
  const feedActive = isForum && !lobbySelected && !searchSelected && !selectedRoomId;
  const showForumFeedSidebar = feedActive;
  const forumManageMode = isForum && lobbySelected;

  if (showForumFeedSidebar) {
    return (
      <PageNav className={forumBoardCss.ForumPageNav}>
        <SpaceHeader />
        <Box grow="Yes" direction="Column" style={{ minHeight: 0, overflow: 'hidden' }}>
          <ForumFeedSidebar />
        </Box>
      </PageNav>
    );
  }

  if (forumManageMode) {
    return (
      <PageNav>
        <SpaceHeader />
      </PageNav>
    );
  }

  const spacePath = getCanonicalAliasOrRoomId(mx, space.roomId);

  return (
    <PageNav>
      <SpaceHeader />
      <PageNavContent scrollRef={scrollRef}>
        <Box direction="Column" gap="300">
          {tombstoneEvent && (
            <SpaceTombstone
              roomId={space.roomId}
              replacementRoomId={tombstoneEvent.getContent().replacement_room}
            />
          )}
          <NavCategory>
            {!isForum && (
              <>
                <NavItem variant="Background" radii="400" aria-selected={lobbySelected}>
                  <NavLink to={getSpaceLobbyPath(spacePath)}>
                    <NavItemContent>
                      <Box as="span" grow="Yes" alignItems="Center" gap="200">
                        <Avatar size="200" radii="400">
                          <Icon src={Icons.Flag} size="100" filled={lobbySelected} />
                        </Avatar>
                        <Box as="span" grow="Yes">
                          <Text as="span" size="Inherit" truncate>
                            Lobby
                          </Text>
                        </Box>
                      </Box>
                    </NavItemContent>
                  </NavLink>
                </NavItem>
                <NavItem variant="Background" radii="400" aria-selected={searchSelected}>
                  <NavLink to={getSpaceSearchPath(spacePath)}>
                    <NavItemContent>
                      <Box as="span" grow="Yes" alignItems="Center" gap="200">
                        <Avatar size="200" radii="400">
                          <Icon src={Icons.Search} size="100" filled={searchSelected} />
                        </Avatar>
                        <Box as="span" grow="Yes">
                          <Text as="span" size="Inherit" truncate>
                            Message Search
                          </Text>
                        </Box>
                      </Box>
                    </NavItemContent>
                  </NavLink>
                </NavItem>
              </>
            )}
            <PluginNavSlot location="channel-list" />
          </NavCategory>
          {navSections.map((section) => (
            <SpaceNavSection
              key={section.categoryId}
              section={section}
              closed={closedCategories.has(section.categoryId)}
              onCategoryClick={handleCategoryClick}
              selectedRoomId={selectedRoomId}
              mDirects={mDirects}
              notificationPreferences={notificationPreferences}
              getToLink={getToLink}
            />
          ))}
        </Box>
      </PageNavContent>
    </PageNav>
  );
}
