import React, { forwardRef, MouseEventHandler, useEffect, useMemo } from 'react';
import { MatrixError, Room } from 'matrix-js-sdk';
import { IHierarchyRoom } from 'matrix-js-sdk/lib/@types/spaces';
import { Box, config, Text } from 'folds';
import {
  HierarchyItem,
  HierarchyItemRoom,
  HierarchyItemSpace,
  isHierarchySpaceRoom,
  useFetchSpaceHierarchyLevel,
} from '../../hooks/useSpaceHierarchy';
import { IPowerLevels } from '../../hooks/usePowerLevels';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { SpaceItemCard } from './SpaceItem';
import { AfterItemDropTarget, CanDropCallback } from './DnD';
import { HierarchyItemMenu } from './HierarchyItemMenu';
import { RoomItemCard } from './RoomItem';
import { StateEvent } from '../../../types/matrix/room';
import { SequenceCard } from '../../components/sequence-card';
import { getRoomCreatorsForRoomId } from '../../hooks/useRoomCreators';
import { getRoomPermissionsAPI } from '../../hooks/useRoomPermissions';
import { PaarrotSubRoomsContent } from '../../hooks/useRoomSubRooms';

type SpaceHierarchyProps = {
  summary: IHierarchyRoom | undefined;
  hierarchySummaries: Map<string, IHierarchyRoom>;
  spaceItem: HierarchyItemSpace;
  roomItems?: HierarchyItemRoom[];
  allJoinedRooms: Set<string>;
  mDirects: Set<string>;
  roomsPowerLevels: Map<string, IPowerLevels>;
  categoryId: string;
  closed: boolean;
  handleClose: MouseEventHandler<HTMLButtonElement>;
  draggingItem?: HierarchyItem;
  onDragging: (item?: HierarchyItem) => void;
  canDrop: CanDropCallback;
  disabledReorder?: boolean;
  nextSpaceId?: string;
  getRoom: (roomId: string) => Room | undefined;
  pinned: boolean;
  togglePinToSidebar: (roomId: string) => void;
  onSpacesFound: (spaceItems: IHierarchyRoom[]) => void;
  onOpenRoom?: MouseEventHandler<HTMLButtonElement>;
};
export const SpaceHierarchy = forwardRef<HTMLDivElement, SpaceHierarchyProps>(
  (
    {
      summary,
      hierarchySummaries,
      spaceItem,
      roomItems,
      allJoinedRooms,
      mDirects,
      roomsPowerLevels,
      categoryId,
      closed,
      handleClose,
      draggingItem,
      onDragging,
      canDrop,
      disabledReorder,
      nextSpaceId,
      getRoom,
      pinned,
      togglePinToSidebar,
      onOpenRoom,
      onSpacesFound,
    },
    ref
  ) => {
    const mx = useMatrixClient();

    const { fetching, error, rooms } = useFetchSpaceHierarchyLevel(spaceItem.roomId, true);

    const subspaces = useMemo(() => {
      const s: Map<string, IHierarchyRoom> = new Map();
      rooms.forEach((r) => {
        if (isHierarchySpaceRoom(r)) {
          s.set(r.room_id, r);
        }
      });
      return s;
    }, [rooms]);

    const spacePowerLevels = roomsPowerLevels.get(spaceItem.roomId);
    const spaceCreators = getRoomCreatorsForRoomId(mx, spaceItem.roomId);
    const spacePermissions =
      spacePowerLevels && getRoomPermissionsAPI(spaceCreators, spacePowerLevels);

    const draggingSpace =
      draggingItem?.roomId === spaceItem.roomId && draggingItem.parentId === spaceItem.parentId;

    const { parentId } = spaceItem;
    const parentPowerLevels = parentId ? roomsPowerLevels.get(parentId) : undefined;
    const parentCreators = parentId ? getRoomCreatorsForRoomId(mx, parentId) : undefined;
    const parentPermissions =
      parentCreators &&
      parentPowerLevels &&
      getRoomPermissionsAPI(parentCreators, parentPowerLevels);

    useEffect(() => {
      onSpacesFound(Array.from(rooms.values()));
    }, [rooms, onSpacesFound]);

    // Build a global set of all sub-room IDs by checking ALL joined rooms
    // This ensures sub-rooms are hidden even if their parent isn't in this space
    const globalSubRoomIds = useMemo(() => {
      const subRoomIds = new Set<string>();
      mx.getRooms().forEach((room) => {
        const subRoomsEvent = room.currentState.getStateEvents(StateEvent.PaarrotSubRooms, '');
        const content = subRoomsEvent?.getContent<PaarrotSubRoomsContent>();
        content?.children?.forEach((childId: string) => subRoomIds.add(childId));
      });
      return subRoomIds;
    }, [mx, allJoinedRooms]);

    // Build a map of parent room -> sub-room IDs for nested rendering (only for rooms in this space)
    const subRoomsMap = useMemo(() => {
      const parentToSubRooms = new Map<string, string[]>();
      roomItems?.forEach((item) => {
        const room = mx.getRoom(item.roomId);
        if (room) {
          const subRoomsEvent = room.currentState.getStateEvents(StateEvent.PaarrotSubRooms, '');
          const content = subRoomsEvent?.getContent<PaarrotSubRoomsContent>();
          const children = content?.children ?? [];
          if (children.length > 0) {
            parentToSubRooms.set(item.roomId, children);
          }
        }
      });
      return parentToSubRooms;
    }, [mx, roomItems]);

    let childItems = roomItems?.filter((i) => !subspaces.has(i.roomId) && !globalSubRoomIds.has(i.roomId));
    // Only hide when we know the user cannot edit children. If power levels are
    // still loading (spacePermissions undefined), keep inaccessible rows visible
    // so admins can clean them up.
    const canEditChild = spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId());
    if (spacePermissions && !canEditChild) {
      // hide unknown rooms for normal user
      childItems = childItems?.filter((i) => {
        const forbidden = error instanceof MatrixError ? error.errcode === 'M_FORBIDDEN' : false;
        const inaccessibleRoom =
          !rooms.get(i.roomId) &&
          !hierarchySummaries.has(i.roomId) &&
          !fetching &&
          (error ? forbidden : true);
        return !inaccessibleRoom;
      });
    }

    return (
      <Box direction="Column" gap="100" ref={ref}>
        <SpaceItemCard
          summary={rooms.get(spaceItem.roomId) ?? summary}
          loading={fetching}
          item={spaceItem}
          joined={allJoinedRooms.has(spaceItem.roomId)}
          categoryId={categoryId}
          closed={closed}
          handleClose={handleClose}
          getRoom={getRoom}
          canEditChild={!!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())}
          canReorder={
            parentPowerLevels && !disabledReorder && parentPermissions
              ? parentPermissions.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
              : false
          }
          options={
            parentId &&
            parentPowerLevels && (
              <HierarchyItemMenu
                item={{ ...spaceItem, parentId }}
                powerLevels={spacePowerLevels}
                joined={allJoinedRooms.has(spaceItem.roomId)}
                canEditChild={
                  !!parentPermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
                }
                pinned={pinned}
                onTogglePin={togglePinToSidebar}
              />
            )
          }
          after={
            <AfterItemDropTarget
              item={spaceItem}
              nextRoomId={closed ? nextSpaceId : childItems?.[0]?.roomId}
              afterSpace
              canDrop={canDrop}
            />
          }
          onDragging={onDragging}
          data-dragging={draggingSpace}
        />
        {childItems && childItems.length > 0 ? (
          <Box direction="Column" gap="100">
            {childItems.map((roomItem, index) => {
              const roomSummary = rooms.get(roomItem.roomId) ?? hierarchySummaries.get(roomItem.roomId);

              const roomPowerLevels = roomsPowerLevels.get(roomItem.roomId) ?? {};

              const lastItem = index === childItems.length;
              const nextRoomId = lastItem ? nextSpaceId : childItems[index + 1]?.roomId;

              const roomDragging =
                draggingItem?.roomId === roomItem.roomId &&
                draggingItem.parentId === roomItem.parentId;

              // Get sub-rooms for this room
              const subRoomIds = subRoomsMap.get(roomItem.roomId) ?? [];

              return (
                <React.Fragment key={roomItem.roomId}>
                  <RoomItemCard
                    item={roomItem}
                    loading={fetching}
                    error={error}
                    summary={roomSummary}
                    dm={mDirects.has(roomItem.roomId)}
                    onOpen={onOpenRoom}
                    getRoom={getRoom}
                    canReorder={
                      !!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId()) &&
                      !disabledReorder
                    }
                    options={
                      <HierarchyItemMenu
                        item={roomItem}
                        powerLevels={roomPowerLevels}
                        joined={allJoinedRooms.has(roomItem.roomId)}
                        canEditChild={
                          !!spacePermissions?.stateEvent(StateEvent.SpaceChild, mx.getSafeUserId())
                        }
                      />
                    }
                    after={
                      <AfterItemDropTarget
                        item={roomItem}
                        nextRoomId={nextRoomId}
                        canDrop={canDrop}
                      />
                    }
                    data-dragging={roomDragging}
                    onDragging={onDragging}
                  />
                  {/* Render sub-rooms nested under this room */}
                  {subRoomIds.length > 0 && (
                    <Box direction="Column" gap="100" style={{ marginLeft: config.space.S400 }}>
                      {subRoomIds.map((subRoomId) => {
                        const subRoom = mx.getRoom(subRoomId);
                        const subRoomSummary = rooms.get(subRoomId) ?? hierarchySummaries.get(subRoomId);
                        const subRoomPowerLevels = roomsPowerLevels.get(subRoomId) ?? {};
                        const subRoomItem: HierarchyItemRoom = {
                          roomId: subRoomId,
                          parentId: roomItem.roomId,
                          content: {},
                        };
                        const subRoomDragging =
                          draggingItem?.roomId === subRoomId &&
                          draggingItem.parentId === roomItem.roomId;

                        // Skip sub-rooms the user hasn't joined yet if we have no summary
                        if (!subRoom && !subRoomSummary) return null;

                        return (
                          <RoomItemCard
                            key={subRoomId}
                            item={subRoomItem}
                            loading={fetching}
                            error={error}
                            summary={subRoomSummary}
                            dm={mDirects.has(subRoomId)}
                            onOpen={onOpenRoom}
                            getRoom={getRoom}
                            canReorder={false}
                            options={
                              <HierarchyItemMenu
                                item={subRoomItem}
                                powerLevels={subRoomPowerLevels}
                                joined={allJoinedRooms.has(subRoomId)}
                                canEditChild={false}
                              />
                            }
                            data-dragging={subRoomDragging}
                            onDragging={onDragging}
                          />
                        );
                      })}
                    </Box>
                  )}
                </React.Fragment>
              );
            })}
          </Box>
        ) : (
          childItems && (
            <SequenceCard variant="SurfaceVariant" gap="300" alignItems="Center">
              <Box
                grow="Yes"
                style={{
                  padding: config.space.S700,
                }}
                direction="Column"
                alignItems="Center"
                justifyContent="Center"
                gap="100"
              >
                <Text size="H5" align="Center">
                  No Rooms
                </Text>
                <Text align="Center" size="T300" priority="300">
                  This space does not contains rooms yet.
                </Text>
              </Box>
            </SequenceCard>
          )
        )}
      </Box>
    );
  }
);
