// Fleet-style info section for the user-profile card.
//
// Each row is a single line: monospace label on the left in a fixed
// gutter, the actual value in the middle (truncates), and an optional
// trailing affordance (copy / open / chevron-menu) on the right. The
// visual is borrowed from the Dawn design bundle's IDE-attribute
// rows — the goal is to fill the card with structured information
// rather than a strip of horizontally-scattered chips.
//
// Each interactive row owns its own popout menu (re-using the
// menu content patterns the legacy `UserChips` exposed). The hosts
// (`UserRoomProfile`) just stitch them together; they don't know
// about menu state.

import React, { MouseEventHandler, useMemo, useState } from 'react';
import {
  Avatar,
  Box,
  Icon,
  Icons,
  Menu,
  MenuItem,
  PopOut,
  RectCords,
  Scroll,
  Spinner,
  Text,
  config,
  toRem,
} from 'folds';
import FocusTrap from 'focus-trap-react';
import { isKeyHotkey } from 'is-hotkey';
import { Room } from 'matrix-js-sdk';
import { useAtomValue } from 'jotai';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { useMutualRooms, useMutualRoomsSupport } from '../../hooks/useMutualRooms';
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
import { useTimeoutToggle } from '../../hooks/useTimeoutToggle';
import { useAllJoinedRoomsSet, useGetRoom } from '../../hooks/useGetRoom';
import { mDirectAtom } from '../../state/mDirectList';
import { useCloseUserRoomProfile } from '../../state/hooks/userRoomProfile';
import { copyToClipboard } from '../../utils/dom';
import { stopPropagation } from '../../utils/keyboard';
import { factoryRoomIdByAtoZ } from '../../utils/sort';
import { openExternalUrl } from '../../utils/capacitor';
import { getMxIdLocalPart, getMxIdServer } from '../../utils/matrix';
import { getDirectRoomAvatarUrl, getRoomAvatarUrl } from '../../utils/room';
import { nameInitials } from '../../utils/common';
import { getExploreServerPath } from '../../pages/pathUtils';
import { getMatrixToUser } from '../../plugins/matrix-to';
import { AsyncStatus } from '../../hooks/useAsyncCallback';
import { RoomAvatar, RoomIcon } from '../room-avatar';
import * as css from './styles.css';

// ── Generic row primitive ────────────────────────────────────────

type InfoRowProps = {
  label: string;
  value: React.ReactNode;
  // Right-side affordance. Either an inline button (e.g. a copy
  // glyph) or a popout-menu trigger pulled from one of the row
  // variants below.
  trailing?: React.ReactNode;
  // Tooltip for the value (typically the full untruncated text).
  title?: string;
};

export function InfoRow({ label, value, trailing, title }: InfoRowProps) {
  return (
    <div className={css.InfoRow}>
      <span className={css.InfoRowLabel}>{label}</span>
      <span className={css.InfoRowValue} title={title}>
        {value}
      </span>
      {trailing && <span className={css.InfoRowTrailing}>{trailing}</span>}
    </div>
  );
}

// ── id (nick + share menu) ───────────────────────────────────────
//
// The value is the LOCALPART only — the server half lives inside the
// full mxid that «Copy user ID» puts on the clipboard. Showing
// `nick:server` here duplicated the server for every same-homeserver
// user and read as protocol noise (user request, redesign item 14).
// The trailing ⋯ menu carries exactly two actions: copy the full
// `@nick:server` and copy the matrix.to link.

export function IdRow({ userId }: { userId: string }) {
  const { t } = useTranslation();
  const [cords, setCords] = useState<RectCords>();
  const [copied, setCopied] = useTimeoutToggle();

  const open: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setCords(evt.currentTarget.getBoundingClientRect());
  };
  const close = () => setCords(undefined);

  const handle = getMxIdLocalPart(userId) ?? userId;

  return (
    <PopOut
      anchor={cords}
      position="Bottom"
      align="End"
      offset={4}
      content={
        <FocusTrap
          focusTrapOptions={{
            initialFocus: false,
            onDeactivate: close,
            clickOutsideDeactivates: true,
            escapeDeactivates: stopPropagation,
            isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
            isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
          }}
        >
          <Menu>
            <div style={{ padding: config.space.S100 }}>
              <MenuItem
                variant="Surface"
                fill="None"
                size="300"
                radii="300"
                onClick={() => {
                  copyToClipboard(userId);
                  setCopied();
                  close();
                }}
              >
                <Text size="B300">{t('User.copy_user_id')}</Text>
              </MenuItem>
              <MenuItem
                variant="Surface"
                fill="None"
                size="300"
                radii="300"
                onClick={() => {
                  copyToClipboard(getMatrixToUser(userId));
                  setCopied();
                  close();
                }}
              >
                <Text size="B300">{t('User.copy_user_link')}</Text>
              </MenuItem>
            </div>
          </Menu>
        </FocusTrap>
      }
    >
      <InfoRow
        label={t('User.row_id')}
        value={handle}
        title={handle}
        trailing={
          <button
            type="button"
            className={css.InfoRowAction}
            onClick={open}
            aria-pressed={!!cords}
            aria-label={t('User.copy_user_id')}
          >
            <Icon size="50" src={copied ? Icons.Check : Icons.HorizontalDots} />
          </button>
        }
      />
    </PopOut>
  );
}

// ── server ───────────────────────────────────────────────────────

export function ServerRow({ userId }: { userId: string }) {
  const { t } = useTranslation();
  const mx = useMatrixClient();
  const navigate = useNavigate();
  const closeProfile = useCloseUserRoomProfile();
  const [cords, setCords] = useState<RectCords>();

  const open: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setCords(evt.currentTarget.getBoundingClientRect());
  };
  const close = () => setCords(undefined);

  const server = getMxIdServer(userId);
  const myServer = getMxIdServer(mx.getSafeUserId());
  if (!server) return null;

  return (
    <PopOut
      anchor={cords}
      position="Bottom"
      align="End"
      offset={4}
      content={
        <FocusTrap
          focusTrapOptions={{
            initialFocus: false,
            onDeactivate: close,
            clickOutsideDeactivates: true,
            escapeDeactivates: stopPropagation,
            isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
            isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
          }}
        >
          <Menu>
            <div style={{ padding: config.space.S100 }}>
              <MenuItem
                variant="Surface"
                fill="None"
                size="300"
                radii="300"
                onClick={() => {
                  copyToClipboard(server);
                  close();
                }}
              >
                <Text size="B300">{t('User.copy_server')}</Text>
              </MenuItem>
              <MenuItem
                variant="Surface"
                fill="None"
                size="300"
                radii="300"
                onClick={() => {
                  navigate(getExploreServerPath(server));
                  closeProfile();
                }}
              >
                <Text size="B300">{t('User.explore_community')}</Text>
              </MenuItem>
              <MenuItem
                variant={myServer === server ? 'Surface' : 'Critical'}
                fill="None"
                size="300"
                radii="300"
                onClick={() => {
                  openExternalUrl(`https://${server}`);
                  close();
                }}
              >
                <Text size="B300">{t('User.open_in_browser')}</Text>
              </MenuItem>
            </div>
          </Menu>
        </FocusTrap>
      }
    >
      <InfoRow
        label={t('User.row_server')}
        value={server}
        title={server}
        trailing={
          <button
            type="button"
            className={css.InfoRowAction}
            onClick={open}
            aria-pressed={!!cords}
            aria-label={t('User.open_in_browser')}
          >
            <Icon size="50" src={Icons.External} />
          </button>
        }
      />
    </PopOut>
  );
}

// ── role (info-only) ─────────────────────────────────────────────

type RoleRowProps = {
  roleLabel: string;
  // Render the role in an accent style — used for creators since
  // they sit above the regular power-level ladder.
  emphasis?: boolean;
};

// Role row — just the localised role label («Admin», «Moderator»,
// «Member», или кастомный tag.name в комнатах со своей PL-таблицей).
// We intentionally don't surface the raw power-level number: «pl N»
// is Matrix protocol jargon that doesn't help end users, and
// duplicates what the role label already conveys for the standard
// 0 / 50 / 100 ladder.
export function RoleRow({ roleLabel, emphasis }: RoleRowProps) {
  const { t } = useTranslation();
  const value = emphasis ? (
    <span className={css.InfoRowAccent}>
      <Icon size="50" src={Icons.Star} filled />
      <span>{roleLabel}</span>
    </span>
  ) : (
    <span>{roleLabel}</span>
  );
  return <InfoRow label={t('User.row_role')} value={value} />;
}

// ── mutual rooms ─────────────────────────────────────────────────

type MutualRoomsData = {
  rooms: Room[];
  spaces: Room[];
  directs: Room[];
};

export function MutualRoomsRow({ userId }: { userId: string }) {
  const { t } = useTranslation();
  const mx = useMatrixClient();
  const supported = useMutualRoomsSupport();
  const state = useMutualRooms(userId);
  const { navigateRoom, navigateSpace } = useRoomNavigate();
  const closeProfile = useCloseUserRoomProfile();
  // Read m.direct directly here, NOT useDirectRooms — the latter
  // returns universal-Direct after P3c, which would collapse the
  // mutual-DMs vs mutual-rooms split that this menu surfaces.
  const mDirects = useAtomValue(mDirectAtom);
  const useAuthentication = useMediaAuthentication();

  const allJoined = useAllJoinedRoomsSet();
  const getRoom = useGetRoom(allJoined);

  const [cords, setCords] = useState<RectCords>();
  const open: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setCords(evt.currentTarget.getBoundingClientRect());
  };
  const close = () => setCords(undefined);

  const mutual: MutualRoomsData = useMemo(() => {
    const data: MutualRoomsData = { rooms: [], spaces: [], directs: [] };
    if (state.status === AsyncStatus.Success) {
      // Copy the array before sorting — `Array.prototype.sort`
      // mutates in place, and `state.data` is the same reference
      // returned by `useMutualRooms` to every caller. Sorting the
      // backing store would silently reorder it for any other
      // consumer of the same response.
      [...state.data]
        .sort(factoryRoomIdByAtoZ(mx))
        .map(getRoom)
        .filter((r): r is Room => !!r)
        .forEach((room) => {
          if (room.isSpaceRoom()) data.spaces.push(room);
          else if (mDirects.has(room.roomId)) data.directs.push(room);
          else data.rooms.push(room);
        });
    }
    return data;
  }, [state, getRoom, mDirects, mx]);

  if (!supported || state.status === AsyncStatus.Error) return null;
  const total = state.status === AsyncStatus.Success ? state.data.length : 0;
  const loading = state.status === AsyncStatus.Loading;

  const renderItem = (room: Room) => {
    const { roomId } = room;
    const dm = mDirects.has(roomId);
    return (
      <MenuItem
        key={roomId}
        variant="Surface"
        fill="None"
        size="300"
        radii="300"
        style={{ paddingLeft: config.space.S100 }}
        onClick={() => {
          if (room.isSpaceRoom()) navigateSpace(roomId);
          else navigateRoom(roomId);
          closeProfile();
        }}
        before={
          <Avatar size="200" radii={dm ? '400' : '300'}>
            {dm || room.isSpaceRoom() ? (
              <RoomAvatar
                roomId={room.roomId}
                src={
                  dm
                    ? getDirectRoomAvatarUrl(mx, room, 96, useAuthentication)
                    : getRoomAvatarUrl(mx, room, 96, useAuthentication)
                }
                alt={room.name}
                renderFallback={() => (
                  <Text as="span" size="H6">
                    {nameInitials(room.name)}
                  </Text>
                )}
              />
            ) : (
              <RoomIcon size="100" joinRule={room.getJoinRule()} roomType={room.getType()} />
            )}
          </Avatar>
        }
      >
        <Text size="B300" truncate>
          {room.name}
        </Text>
      </MenuItem>
    );
  };

  const value = loading ? (
    <span className={css.InfoRowMixed}>
      <Spinner size="50" />
      <span>{t('User.row_mutual_loading')}</span>
    </span>
  ) : (
    t('User.row_mutual_count', { count: total })
  );

  return (
    <PopOut
      anchor={cords}
      position="Bottom"
      align="End"
      offset={4}
      content={
        state.status === AsyncStatus.Success ? (
          <FocusTrap
            focusTrapOptions={{
              initialFocus: false,
              onDeactivate: close,
              clickOutsideDeactivates: true,
              escapeDeactivates: stopPropagation,
              isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
              isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
            }}
          >
            <Menu style={{ display: 'flex', maxWidth: toRem(220), maxHeight: '80vh' }}>
              <Box grow="Yes">
                <Scroll size="300" hideTrack>
                  <Box
                    direction="Column"
                    gap="400"
                    style={{ padding: config.space.S200, paddingRight: 0 }}
                  >
                    {mutual.spaces.length > 0 && (
                      <Box direction="Column" gap="100">
                        <Text style={{ paddingLeft: config.space.S100 }} size="L400">
                          {t('User.row_mutual_spaces')}
                        </Text>
                        {mutual.spaces.map(renderItem)}
                      </Box>
                    )}
                    {mutual.rooms.length > 0 && (
                      <Box direction="Column" gap="100">
                        <Text style={{ paddingLeft: config.space.S100 }} size="L400">
                          {t('User.row_mutual_rooms')}
                        </Text>
                        {mutual.rooms.map(renderItem)}
                      </Box>
                    )}
                    {mutual.directs.length > 0 && (
                      <Box direction="Column" gap="100">
                        <Text style={{ paddingLeft: config.space.S100 }} size="L400">
                          {t('User.row_mutual_dms')}
                        </Text>
                        {mutual.directs.map(renderItem)}
                      </Box>
                    )}
                  </Box>
                </Scroll>
              </Box>
            </Menu>
          </FocusTrap>
        ) : null
      }
    >
      <InfoRow
        label={t('User.row_mutual')}
        value={value}
        trailing={
          <button
            type="button"
            className={css.InfoRowAction}
            onClick={open}
            aria-pressed={!!cords}
            disabled={loading || total === 0}
            aria-label={t('User.row_mutual')}
          >
            <Icon size="50" src={Icons.ChevronBottom} />
          </button>
        }
      />
    </PopOut>
  );
}
