import React, { MouseEventHandler, forwardRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Room } from 'matrix-js-sdk/lib/models/room';
import {
  Avatar,
  Badge,
  Box,
  Icon,
  IconButton,
  Icons,
  Line,
  Menu,
  MenuItem,
  PopOut,
  RectCords,
  Spinner,
  Text,
  config,
  toRem,
} from 'folds';
import { useFocusWithin, useHover } from 'react-aria';
import FocusTrap from 'focus-trap-react';
import { useAtom, useAtomValue } from 'jotai';
import { NavItem, NavItemContent, NavItemOptions, NavLink } from '../../components/nav';
import { UnreadBadge, UnreadBadgeCenter } from '../../components/unread-badge';
import { RoomAvatar } from '../../components/room-avatar';
import {
  getDirectRoomAvatarUrl,
  getRoomAvatarUrl,
  getStateEvent,
  isOneOnOneRoom,
} 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 { getPowersLevelFromMatrixEvent, usePowerLevels } from '../../hooks/usePowerLevels';
import { copyToClipboard } from '../../utils/dom';
import { markAsRead } from '../../utils/notifications';
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 } from '../../utils/matrix';
import { getViaServers } from '../../plugins/via-servers';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { useSetting } from '../../state/hooks/settings';
import { settingsAtom } from '../../state/settings';
import { useOpenRoomSettings } from '../../state/hooks/roomSettings';
import { useSpaceOptionally } from '../../hooks/useSpace';
import {
  RoomNotificationMode,
  getRoomNotificationModeIcon,
} from '../../hooks/useRoomsNotificationPreferences';
import { RoomNotificationModeSwitcher } from '../../components/RoomNotificationSwitcher';
import { getRoomCreatorsForRoomId, useRoomCreators } from '../../hooks/useRoomCreators';
import { getRoomPermissionsAPI, useRoomPermissions } from '../../hooks/useRoomPermissions';
import { InviteUserPrompt } from '../../components/invite-user-prompt';
import { useRoomName } from '../../hooks/useRoomMeta';
import { useCallMembers, useCallSession } from '../../hooks/useCall';
import { useCallEmbed, useCallStart } from '../../hooks/useCallEmbed';
import { callChatAtom } from '../../state/callEmbed';
import { useCallPreferencesAtom } from '../../state/hooks/callPreferences';
import { useAutoDiscoveryInfo } from '../../hooks/useAutoDiscoveryInfo';
import { livekitSupport } from '../../hooks/useLivekitSupport';
import { StateEvent } from '../../../types/matrix/room';
import { Presence, useUserPresence } from '../../hooks/useUserPresence';
import { timeHourMinute } from '../../utils/time';
import * as css from './styles.css';

type DmStreamRowMenuProps = {
  room: Room;
  requestClose: () => void;
  notificationMode?: RoomNotificationMode;
};
const DmStreamRowMenu = forwardRef<HTMLDivElement, DmStreamRowMenuProps>(
  ({ room, requestClose, notificationMode }, ref) => {
    const { t } = useTranslation();
    const mx = useMatrixClient();
    const [hideActivity] = useSetting(settingsAtom, 'hideActivity');
    const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
    const powerLevels = usePowerLevels(room);
    const creators = useRoomCreators(room);

    const permissions = useRoomPermissions(creators, powerLevels);
    const canInvite = permissions.action('invite', mx.getSafeUserId());
    const openRoomSettings = useOpenRoomSettings();
    const space = useSpaceOptionally();

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

    const handleMarkAsRead = () => {
      markAsRead(mx, 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();
    };

    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>
              {t('Home.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>
                  {t('Home.notifications')}
                </Text>
              </MenuItem>
            )}
          </RoomNotificationModeSwitcher>
        </Box>
        <Line variant="Surface" size="300" />
        <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
          <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>
              {t('Home.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>
              {t('Home.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>
              {t('Home.room_settings')}
            </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={() => 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>
                    {t('Home.leave_room')}
                  </Text>
                </MenuItem>
                {promptLeave && (
                  <LeaveRoomPrompt
                    roomId={room.roomId}
                    onDone={requestClose}
                    onCancel={() => setPromptLeave(false)}
                  />
                )}
              </>
            )}
          </UseStateProvider>
        </Box>
      </Menu>
    );
  }
);

function CallChatToggle() {
  const { t } = useTranslation();
  const [chat, setChat] = useAtom(callChatAtom);

  return (
    <IconButton
      onClick={() => setChat(!chat)}
      aria-pressed={chat}
      aria-label={t('Home.toggle_chat')}
      variant="Background"
      fill="None"
      size="300"
      radii="300"
    >
      <Icon size="50" src={Icons.Message} filled={chat} />
    </IconButton>
  );
}

type RoomPreview = { ts: number | undefined; text: string };

const deriveRoomPreview = (room: Room): RoomPreview => {
  const events = room.getLiveTimeline().getEvents();
  for (let i = events.length - 1; i >= 0; i -= 1) {
    const ev = events[i];
    if (ev.getType() === 'm.room.message' && !ev.isRedacted()) {
      const content = ev.getContent();
      const body = typeof content?.body === 'string' ? content.body.trim() : '';
      if (body) {
        return { ts: ev.getTs(), text: body.replace(/\s+/g, ' ') };
      }
    }
  }
  return { ts: room.getLastActiveTimestamp() || undefined, text: '' };
};

const formatRowTime = (ts: number): string => {
  const now = Date.now();
  const diff = now - ts;
  const day = 24 * 60 * 60 * 1000;
  const date = new Date(ts);
  const today = new Date();
  const sameDay =
    date.getFullYear() === today.getFullYear() &&
    date.getMonth() === today.getMonth() &&
    date.getDate() === today.getDate();
  if (sameDay) return timeHourMinute(ts);
  if (diff < 7 * day) {
    return date.toLocaleDateString(undefined, { weekday: 'short' });
  }
  return date.toLocaleDateString(undefined, { day: 'numeric', month: 'short' });
};

type DmStreamRowProps = {
  room: Room;
  selected: boolean;
  linkPath: string;
  notificationMode?: RoomNotificationMode;
};
export function DmStreamRow({ room, selected, notificationMode, linkPath }: DmStreamRowProps) {
  const { t } = useTranslation();
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  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 typingMember = useRoomTypingMember(room.roomId).filter(
    (receipt) => receipt.userId !== mx.getUserId()
  );

  const roomName = useRoomName(room);

  // Peer online dot — mirrors the profile-card HeroOnlineDot. Only 1:1 rooms
  // resolve a single peer; group rooms have no single presence to surface.
  // `guessDmRoomUserId`'s last fallback is my own id, so drop that case — the
  // empty string makes `useUserPresence` a no-op rather than reading my own
  // presence as if I were the peer.
  const myUserId = mx.getSafeUserId();
  const oneOnOne = isOneOnOneRoom(room);
  const peerCandidate = oneOnOne ? guessDmRoomUserId(room, myUserId) : undefined;
  const peerUserId = peerCandidate && peerCandidate !== myUserId ? peerCandidate : undefined;
  const peerPresence = useUserPresence(peerUserId ?? '');
  const peerOnline = !!peerUserId && peerPresence?.presence === Presence.Online;

  const preview = deriveRoomPreview(room);
  const previewText = preview.text;
  const previewTs = preview.ts;
  const timeLabel = previewTs ? formatRowTime(previewTs) : '';

  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 optionsVisible = hover || !!menuAnchor;
  const callSession = useCallSession(room);
  const callMembers = useCallMembers(room, callSession);
  const startCall = useCallStart(true);
  const callEmbed = useCallEmbed();
  const callPref = useAtomValue(useCallPreferencesAtom());
  const autoDiscoveryInfo = useAutoDiscoveryInfo();

  const handleStartCall: MouseEventHandler<HTMLAnchorElement> = (evt) => {
    // Read call permission only when actually starting a call (not reactively).
    // Mirrors RoomNavItem.handleStartCall — both are call-start sinks and
    // useCallStart/createCallEmbed enforce no permission of their own.
    const powerLevelsEvent = getStateEvent(room, StateEvent.RoomPowerLevels);
    const powerLevels = getPowersLevelFromMatrixEvent(powerLevelsEvent);
    const creators = getRoomCreatorsForRoomId(mx, room.roomId);
    const permissions = getRoomPermissionsAPI(creators, powerLevels);
    const hasCallPermission = permissions.event(
      StateEvent.GroupCallMemberPrefix,
      mx.getSafeUserId()
    );

    if (!hasCallPermission || (!livekitSupport(autoDiscoveryInfo) && callMembers.length === 0)) {
      return;
    }
    if (callEmbed) {
      return;
    }
    if (selected) {
      evt.preventDefault();
      startCall(room, callPref);
    }
  };

  return (
    <NavItem
      variant="Background"
      radii="400"
      highlight={unread !== undefined}
      aria-selected={selected}
      data-hover={!!menuAnchor}
      onContextMenu={handleContextMenu}
      className={css.DmRowOuter}
      {...hoverProps}
      {...focusWithinProps}
    >
      <NavLink to={linkPath} onClick={room.isCallRoom() ? handleStartCall : undefined}>
        <NavItemContent>
          <Box as="span" grow="Yes" alignItems="Center" gap="300" className={css.DmRowInner}>
            <span className={css.DmRowAvatar}>
              <Avatar size="500" radii="500" style={{ width: toRem(48), height: toRem(48) }}>
                <RoomAvatar
                  roomId={room.roomId}
                  src={
                    oneOnOne
                      ? // 1:1 — fall back to the peer's avatar when the room
                        // itself has no custom one. Mirrors RoomViewHeader's
                        // `useRoomAvatar(room, isOneOnOne)` semantics.
                        getDirectRoomAvatarUrl(mx, room, 96, useAuthentication)
                      : // Group / non-1:1 — use the room avatar only. Without
                        // this guard `getDirectRoomAvatarUrl` would surface the
                        // first non-self member's avatar in groups without a
                        // custom room avatar, making them look like 1:1 chats.
                        getRoomAvatarUrl(mx, room, 96, useAuthentication)
                  }
                  alt={roomName}
                  renderFallback={() => (
                    <Text as="span" size="H5">
                      {nameInitials(roomName)}
                    </Text>
                  )}
                />
              </Avatar>
              {peerOnline && <span className={css.DmRowOnlineDot} aria-hidden />}
            </span>
            <Box as="span" direction="Column" grow="Yes" gap="100" className={css.DmRowText}>
              <Box as="span" alignItems="Baseline" gap="200">
                <Box as="span" grow="Yes" className={css.DmRowTextStretch}>
                  <Text
                    as="span"
                    priority={unread ? '500' : '400'}
                    size="T300"
                    truncate
                    className={css.DmRowName}
                  >
                    {roomName}
                  </Text>
                </Box>
                {timeLabel && !optionsVisible && <span className={css.DmRowTime}>{timeLabel}</span>}
              </Box>
              <Box as="span" alignItems="Center" gap="200">
                <Box as="span" grow="Yes" className={css.DmRowTextStretch}>
                  {typingMember.length > 0 ? (
                    <TypingIndicator size="300" disableAnimation />
                  ) : (
                    <Text
                      as="span"
                      size="T200"
                      truncate
                      className={unread ? css.DmRowPreviewUnread : css.DmRowPreview}
                    >
                      {previewText}
                    </Text>
                  )}
                </Box>
                {!optionsVisible && unread && (
                  <UnreadBadgeCenter>
                    <UnreadBadge highlight={unread.highlight > 0} count={unread.total} />
                  </UnreadBadgeCenter>
                )}
                {!optionsVisible && notificationMode !== RoomNotificationMode.Unset && (
                  <Icon
                    size="50"
                    src={getRoomNotificationModeIcon(notificationMode)}
                    aria-label={notificationMode}
                  />
                )}
                {room.isCallRoom() && callMembers.length > 0 && (
                  <Badge variant="Critical" fill="Solid" size="400">
                    <Text as="span" size="L400" truncate>
                      {t('Home.live_count', { count: callMembers.length })}
                    </Text>
                  </Badge>
                )}
              </Box>
            </Box>
          </Box>
        </NavItemContent>
      </NavLink>
      {optionsVisible && (
        <NavItemOptions>
          {selected && (callEmbed?.roomId === room.roomId || room.isCallRoom()) && (
            <CallChatToggle />
          )}
          <PopOut
            id={`menu-${room.roomId}`}
            aria-expanded={!!menuAnchor}
            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,
                }}
              >
                <DmStreamRowMenu
                  room={room}
                  requestClose={() => setMenuAnchor(undefined)}
                  notificationMode={notificationMode}
                />
              </FocusTrap>
            }
          >
            <IconButton
              onClick={handleOpenMenu}
              aria-pressed={!!menuAnchor}
              aria-controls={`menu-${room.roomId}`}
              aria-label="More Options"
              variant="Background"
              fill="None"
              size="300"
              radii="300"
            >
              <Icon size="50" src={Icons.VerticalDots} />
            </IconButton>
          </PopOut>
        </NavItemOptions>
      )}
    </NavItem>
  );
}
