import React, { MouseEventHandler, forwardRef, useState, useMemo } from 'react';
import { Room, EventTimeline } from 'matrix-js-sdk';
import { useAtomValue } from 'jotai';
import { useNavigate } from 'react-router-dom';
import { Avatar, Box, IconButton, Text, Menu, MenuItem, config, PopOut, toRem, Line, RectCords, Badge, Spinner, Tooltip, TooltipProvider } from 'folds';
import { Icon, Icons } from '../../components/icons';
import { useFocusWithin, useHover } from 'react-aria';
import FocusTrap from 'focus-trap-react';
import { NavItem, NavItemContent, NavItemOptions, NavLink } from '../../components/nav';
import * as css from '../../components/nav/styles.css';
import { UnreadBadge, UnreadBadgeCenter } from '../../components/unread-badge';
import { RoomAvatar, RoomIcon } from '../../components/room-avatar';
import { getDirectRoomAvatarUrl, getRoomAvatarUrl, guessPerfectParent, getOrphanParents } from '../../utils/room';
import { nameInitials } from '../../utils/common';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useRoomUnread } from '../../state/hooks/unread';
import { roomToUnreadAtom } from '../../state/room/roomToUnread';
import { usePowerLevels } from '../../hooks/usePowerLevels';
import { copyToClipboard } from '../../utils/dom';
import { useMarkAsRead } from '../../hooks/useMarkAsRead';
import { UseStateProvider } from '../../components/UseStateProvider';
import { LeaveRoomPrompt } from '../../components/leave-room-prompt';
import { useRoomTypingMember } from '../../hooks/useRoomTypingMembers';
import { TypingIndicator } from '../../components/typing-indicator';
import { stopPropagation } from '../../utils/keyboard';
import { getMatrixToRoom } from '../../plugins/matrix-to';
import { getCanonicalAliasOrRoomId, guessDmRoomUserId, isRoomAlias, mxcUrlToHttp } from '../../utils/matrix';
import { getViaServers } from '../../plugins/via-servers';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { useSetting } from '../../state/hooks/settings';
import { settingsAtom, ScrollToLatestBehavior } from '../../state/settings';
import { useOpenRoomSettings } from '../../state/hooks/roomSettings';
import { useSpaceOptionally } from '../../hooks/useSpace';
import {
  getRoomNotificationModeIcon,
  RoomNotificationMode,
} from '../../hooks/useRoomsNotificationPreferences';
import { RoomNotificationModeSwitcher } from '../../components/RoomNotificationSwitcher';
import { useRoomCreators } from '../../hooks/useRoomCreators';
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
import { InviteUserPrompt } from '../../components/invite-user-prompt';
import { CallParticipantsIndicator } from './CallParticipantsIndicator';
import { useCall, useRoomCall } from '../call/useCall';
import { CallType } from '../call/types';
import { roomToParentsAtom } from '../../state/room/roomToParents';
import { StateEvent } from '../../../types/matrix/room';
import { mDirectAtom } from '../../state/mDirectList';
import { useOpenCreateSubRoomModal } from '../../state/hooks/createRoomModal';
import { PresenceAvatar } from '../../components/presence';
import { Presence, useUserPresence } from '../../hooks/useUserPresence';

type RoomNavItemMenuProps = {
  room: Room;
  requestClose: () => void;
  notificationMode?: RoomNotificationMode;
};
const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
  ({ room, requestClose, notificationMode }, ref) => {
    const mx = useMatrixClient();
    const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
    const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
    const powerLevels = usePowerLevels(room);
    const creators = useRoomCreators(room);
    const { startCall: startCallGlobal, endCall, activeCall } = useCall();
    const { callSupported } = useRoomCall(room.roomId);
    const markAsRead = useMarkAsRead(mx);

    const permissions = useRoomPermissions(creators, powerLevels);
    const canInvite = permissions.action('invite', mx.getSafeUserId());
    const canAddSubRoom = permissions.stateEvent(StateEvent.PaarrotSubRooms, mx.getSafeUserId());
    const openRoomSettings = useOpenRoomSettings();
    const openCreateSubRoom = useOpenCreateSubRoomModal();
    const space = useSpaceOptionally();

    const [invitePrompt, setInvitePrompt] = useState(false);

    const handleMarkAsRead = () => {
      markAsRead(room.roomId, hideActivity);
      requestClose();
    };

    const handleInvite = () => {
      setInvitePrompt(true);
    };

    const handleCopyLink = () => {
      const roomIdOrAlias = getCanonicalAliasOrRoomId(mx, room.roomId);
      const viaServers = isRoomAlias(roomIdOrAlias) ? undefined : getViaServers(room);
      copyToClipboard(getMatrixToRoom(roomIdOrAlias, viaServers));
      requestClose();
    };

    const handleRoomSettings = () => {
      openRoomSettings(room.roomId, space?.roomId);
      requestClose();
    };

    const handleAddSubRoom = () => {
      openCreateSubRoom(room.roomId);
      requestClose();
    };

    const handleJoinVoice = async () => {
      try {
        // If already in a call, leave it first
        if (activeCall) {
          await endCall();
        }
        await startCallGlobal(room.roomId, CallType.Voice);
        requestClose();
      } catch (error) {
        console.error('Failed to join voice call:', error);
      }
    };

    return (
      <Menu ref={ref} style={{ maxWidth: toRem(160), width: '100vw' }}>
        {invitePrompt && room && (
          <InviteUserPrompt
            room={room}
            requestClose={() => {
              setInvitePrompt(false);
              requestClose();
            }}
          />
        )}
        <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
          <MenuItem
            onClick={handleMarkAsRead}
            size="300"
            after={<Icon size="100" src={Icons.CheckTwice} />}
            radii="300"
            disabled={!unread}
          >
            <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
              Mark as Read
            </Text>
          </MenuItem>
          <RoomNotificationModeSwitcher roomId={room.roomId} value={notificationMode}>
            {(handleOpen, opened, changing) => (
              <MenuItem
                size="300"
                after={
                  changing ? (
                    <Spinner size="100" variant="Secondary" />
                  ) : (
                    <Icon size="100" src={getRoomNotificationModeIcon(notificationMode)} />
                  )
                }
                radii="300"
                aria-pressed={opened}
                onClick={handleOpen}
              >
                <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
                  Notifications
                </Text>
              </MenuItem>
            )}
          </RoomNotificationModeSwitcher>
        </Box>
        <Line variant="Surface" size="300" />
        <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
          <MenuItem
            onClick={handleJoinVoice}
            variant="Primary"
            fill="None"
            size="300"
            after={<Icon size="100" src={Icons.VolumeHigh} />}
            radii="300"
            disabled={!callSupported}
          >
            <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
              Join Voice
            </Text>
          </MenuItem>
          <MenuItem
            onClick={handleInvite}
            variant="Primary"
            fill="None"
            size="300"
            after={<Icon size="100" src={Icons.UserPlus} />}
            radii="300"
            aria-pressed={invitePrompt}
            disabled={!canInvite}
          >
            <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
              Invite
            </Text>
          </MenuItem>
          <MenuItem
            onClick={handleCopyLink}
            size="300"
            after={<Icon size="100" src={Icons.Link} />}
            radii="300"
          >
            <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
              Copy Link
            </Text>
          </MenuItem>
          <MenuItem
            onClick={handleRoomSettings}
            size="300"
            after={<Icon size="100" src={Icons.Setting} />}
            radii="300"
          >
            <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
              Room Settings
            </Text>
          </MenuItem>
          <MenuItem
            onClick={handleAddSubRoom}
            size="300"
            after={<Icon size="100" src={Icons.Plus} />}
            radii="300"
            disabled={!canAddSubRoom}
          >
            <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
              Add Sub-Room
            </Text>
          </MenuItem>
        </Box>
        <Line variant="Surface" size="300" />
        <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
          <UseStateProvider initial={false}>
            {(promptLeave, setPromptLeave) => (
              <>
                <MenuItem
                  onClick={(evt) => {
                    if (evt.shiftKey) {
                      mx.leave(room.roomId);
                      requestClose();
                    } else {
                      setPromptLeave(true);
                    }
                  }}
                  variant="Critical"
                  fill="None"
                  size="300"
                  after={<Icon size="100" src={Icons.ArrowGoLeft} />}
                  radii="300"
                  aria-pressed={promptLeave}
                >
                  <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
                    Leave Room
                  </Text>
                </MenuItem>
                {promptLeave && (
                  <LeaveRoomPrompt
                    roomId={room.roomId}
                    onDone={requestClose}
                    onCancel={() => setPromptLeave(false)}
                  />
                )}
              </>
            )}
          </UseStateProvider>
        </Box>
      </Menu>
    );
  }
);

type RoomNavItemProps = {
  room: Room;
  selected: boolean;
  linkPath: string;
  notificationMode?: RoomNotificationMode;
  showAvatar?: boolean;
  direct?: boolean;
  hideIcon?: boolean;
};
export function RoomNavItem({
  room,
  selected,
  showAvatar,
  direct,
  notificationMode,
  linkPath,
  hideIcon,
}: RoomNavItemProps) {
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const navigate = useNavigate();
  const [scrollToLatestBehavior] = useSetting(settingsAtom, 'scrollToLatestBehavior');
  const [hover, setHover] = useState(false);
  const { hoverProps } = useHover({ onHoverChange: setHover });
  const { focusWithinProps } = useFocusWithin({ onFocusWithinChange: setHover });
  const [menuAnchor, setMenuAnchor] = useState<RectCords>();
  const unread = useRoomUnread(room.roomId, roomToUnreadAtom);

  const avatarUrl = direct
    ? getDirectRoomAvatarUrl(mx, room, 96, useAuthentication)
    : getRoomAvatarUrl(mx, room, 96, useAuthentication);

  const mDirects = useAtomValue(mDirectAtom);
  const isDirect = Boolean(direct || mDirects.has(room.roomId));
  const dmUserId = isDirect ? guessDmRoomUserId(room, mx.getSafeUserId()) : undefined;
  const dmPresence = useUserPresence(dmUserId ?? '');
  
  const shouldShowIcon = !hideIcon || (hideIcon && avatarUrl);
  const typingMember = useRoomTypingMember(room.roomId).filter(
    (receipt) => receipt.userId !== mx.getUserId()
  );
  const roomToParents = useAtomValue(roomToParentsAtom);

  // Get first participated thread for preview
  const firstThreadPreview = useMemo(() => {
    try {
      const threads = room.getThreads();
      const participatedThreads = threads.filter((thread) => thread.hasCurrentUserParticipated);
      if (participatedThreads.length === 0) return undefined;

      const firstThread = participatedThreads[0];
      const rootEvent = firstThread.rootEvent;
      if (!rootEvent) return undefined;

      const rootBody = rootEvent.getContent()?.body ?? '';
      return rootBody;
    } catch (error) {
      return undefined;
    }
  }, [room]);

  // Get parent space for DMs
  const parentSpaceInfo = (() => {
    if (!isDirect || !mDirects.has(room.roomId)) return undefined;
    
    const orphanParents = getOrphanParents(roomToParents, room.roomId);
    if (orphanParents.length === 0) return undefined;
    
    const parentSpaceId = guessPerfectParent(mx, room.roomId, orphanParents) ?? orphanParents[0];
    const parentSpace = mx.getRoom(parentSpaceId);
    if (!parentSpace) return undefined;

    const avatarEvent = parentSpace.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getStateEvents(StateEvent.RoomAvatar, '');
    const avatarContent = avatarEvent?.getContent();
    const avatarMxc = avatarContent && typeof avatarContent.url === 'string' ? avatarContent.url : undefined;
    const avatarUrl = avatarMxc ? mxcUrlToHttp(mx, avatarMxc, useAuthentication, 96, 96, 'crop') ?? undefined : undefined;

    return {
      name: parentSpace.name,
      avatarUrl,
      roomId: parentSpace.roomId,
    };
  })();
  const handleContextMenu: MouseEventHandler<HTMLElement> = (evt) => {
    evt.preventDefault();
    setMenuAnchor({
      x: evt.clientX,
      y: evt.clientY,
      width: 0,
      height: 0,
    });
  };

  const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setMenuAnchor(evt.currentTarget.getBoundingClientRect());
  };

  const handleRoomClick: MouseEventHandler<HTMLAnchorElement> = (evt) => {
    if (selected) {
      evt.preventDefault();
      
      const shouldScroll = 
        scrollToLatestBehavior === ScrollToLatestBehavior.Always ||
        (scrollToLatestBehavior === ScrollToLatestBehavior.OnNewMessage && unread !== undefined);
      
      if (shouldScroll) {
        navigate(linkPath, { replace: true, state: { scrollToLatest: Date.now() } });
      }
    }
  };

  const optionsVisible = hover || !!menuAnchor;

  return (
    <Box direction="Column" style={{ width: '100%' }}>
      <NavItem
        className={!shouldShowIcon ? css.ThreadItem : undefined}
        variant="Background"
        radii="400"
        highlight={unread !== undefined}
        aria-selected={selected}
        data-hover={!!menuAnchor}
        onContextMenu={handleContextMenu}
        {...hoverProps}
        {...focusWithinProps}
      >
        <NavLink to={linkPath} onClick={handleRoomClick}>
          <NavItemContent>
            <Box as="span" grow="Yes" alignItems="Center" gap={shouldShowIcon ? "200" : "100"}>
              {shouldShowIcon &&
                (isDirect ? (
                  <PresenceAvatar
                    presence={
                      dmUserId && dmUserId !== mx.getSafeUserId()
                        ? dmPresence?.presence ?? Presence.Offline
                        : Presence.Offline
                    }
                  >
                    <Avatar size="200" radii="400">
                      {showAvatar || avatarUrl ? (
                        <RoomAvatar
                          roomId={room.roomId}
                          src={avatarUrl}
                          alt={room.name}
                          renderFallback={() => (
                            <Text as="span" size="H6">
                              {nameInitials(room.name)}
                            </Text>
                          )}
                        />
                      ) : (
                        <RoomIcon
                          style={{ opacity: unread ? config.opacity.P500 : config.opacity.P300 }}
                          filled={selected}
                          size="100"
                          joinRule={room.getJoinRule()}
                        />
                      )}
                    </Avatar>
                  </PresenceAvatar>
                ) : (
                  <Avatar size="200" radii="400">
                    {showAvatar || avatarUrl ? (
                      <RoomAvatar
                        roomId={room.roomId}
                        src={avatarUrl}
                        alt={room.name}
                        renderFallback={() => (
                          <Text as="span" size="H6">
                            {nameInitials(room.name)}
                          </Text>
                        )}
                      />
                    ) : (
                      <RoomIcon
                        style={{ opacity: unread ? config.opacity.P500 : config.opacity.P300 }}
                        filled={selected}
                        size="100"
                        joinRule={room.getJoinRule()}
                      />
                    )}
                  </Avatar>
                ))}
              <Box as="span" grow="Yes" direction="Column" style={{ overflow: 'hidden', minWidth: 0 }}>
                <Text priority={unread ? '500' : '300'} as="span" size="Inherit" truncate>
                  {room.name}
                </Text>
                {firstThreadPreview && (
                  <Text as="span" size="T200" priority="300" truncate>
                    ╰ {firstThreadPreview}
                  </Text>
                )}
              </Box>
              {!optionsVisible && !unread && !selected && typingMember.length > 0 && (
                <Badge size="300" variant="Secondary" fill="Soft" radii="Pill" outlined>
                  <TypingIndicator size="300" disableAnimation />
                </Badge>
              )}
              {!optionsVisible && unread && (
                <UnreadBadgeCenter>
                  <UnreadBadge highlight={unread.highlight > 0} count={unread.total} />
                </UnreadBadgeCenter>
              )}
              {!optionsVisible && notificationMode !== RoomNotificationMode.Unset && (
                <Icon size="50" src={getRoomNotificationModeIcon(notificationMode)} />
              )}
            </Box>
          </NavItemContent>
        </NavLink>
      {optionsVisible && (
        <NavItemOptions>
          <Box alignItems="Center" gap="100">
            {parentSpaceInfo && (
              <TooltipProvider
                position="Top"
                tooltip={
                  <Tooltip>
                    <Text size="T200">Space: {parentSpaceInfo.name}</Text>
                  </Tooltip>
                }
              >
                {(triggerRef) => (
                  <Avatar ref={triggerRef} size="200" radii="Pill" style={{ flexShrink: 0, width: '16px', height: '16px' }}>
                    <RoomAvatar
                      roomId={parentSpaceInfo.roomId}
                      src={parentSpaceInfo.avatarUrl}
                      alt={parentSpaceInfo.name}
                      renderFallback={() => (
                        <Text size="Inherit">
                          {nameInitials(parentSpaceInfo.name, 1)}
                        </Text>
                      )}
                    />
                  </Avatar>
                )}
              </TooltipProvider>
            )}
            {optionsVisible && (
              <PopOut
                anchor={menuAnchor}
                offset={menuAnchor?.width === 0 ? 0 : undefined}
                alignOffset={menuAnchor?.width === 0 ? 0 : -5}
                position="Bottom"
                align={menuAnchor?.width === 0 ? 'Start' : 'End'}
                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,
                    }}
                  >
                    <RoomNavItemMenu
                      room={room}
                      requestClose={() => setMenuAnchor(undefined)}
                      notificationMode={notificationMode}
                    />
                  </FocusTrap>
                }
              >
                <IconButton
                  onClick={handleOpenMenu}
                  aria-pressed={!!menuAnchor}
                  variant="Background"
                  fill="None"
                  size="300"
                  radii="300"
                >
                  <Icon size="50" src={Icons.VerticalDots} />
                </IconButton>
              </PopOut>
            )}
          </Box>
        </NavItemOptions>
      )}
      </NavItem>
      <CallParticipantsIndicator roomId={room.roomId} />
    </Box>
  );
}
