import React, { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Avatar, Box, Icon, IconButton, Icons, Scroll, Spinner, Text, color, toRem } from 'folds';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
import { RoomAvatar, RoomIcon } from '../../../components/room-avatar';
import { UserAvatar } from '../../../components/user-avatar';
import { UnreadBadge, UnreadBadgeCenter } from '../../../components/unread-badge';
import {
  getAllParents,
  getDirectRoomAvatarUrl,
  getRoomAvatarUrl,
  guessPerfectParent,
} from '../../../utils/room';
import { nameInitials } from '../../../utils/common';
import { getMxIdLocalPart, getMxIdServer, mxcUrlToHttp } from '../../../utils/matrix';
import { getDmUserId, useRoomSearch } from '../../../features/search/useRoomSearch';
import { StartDirectDialog } from '../../../features/search/StartDirectDialog';
import { SEARCH_FORM_BASE_PX } from '../geometry';

// Shared inline result-row chrome (rooms / people / address card).
const ROW_BASE_STYLE: React.CSSProperties = {
  appearance: 'none',
  WebkitAppearance: 'none',
  border: 'none',
  color: color.Background.OnContainer,
  borderRadius: toRem(12),
  padding: `${toRem(8)} ${toRem(10)}`,
  width: '100%',
  display: 'flex',
  alignItems: 'center',
  gap: toRem(10),
  cursor: 'pointer',
  textAlign: 'left',
  font: 'inherit',
};

type Props = {
  onClose: () => void;
};

// Inline search panel mounted in the StreamHeader. Shares all search
// logic with the global Search modal via `useRoomSearch`; only the
// presentation chrome differs (no Modal/Overlay/FocusTrap, a custom
// row layout that matches the inline aesthetic). Surfaces both your
// existing rooms/DMs and "People" from the homeserver user directory —
// tapping a person opens or creates a DM.
export function InlineRoomSearch({ onClose }: Props) {
  const { t } = useTranslation();
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const { navigateRoom, navigateSpace } = useRoomNavigate();

  const openRoomId = useCallback(
    (roomId: string, isSpace: boolean) => {
      if (isSpace) navigateSpace(roomId);
      else navigateRoom(roomId);
      onClose();
    },
    [navigateRoom, navigateSpace, onClose]
  );

  const {
    inputRef,
    scrollRef,
    roomsToRender,
    result,
    listFocus,
    query,
    clearSearch,
    handleInputChange,
    handleInputKeyDown,
    handleRoomClick,
    peopleToRender,
    directoryLoading,
    requestDirect,
    creatingUserId,
    addressPreview,
    dmError,
    pendingDirect,
    confirmDirect,
    cancelDirect,
    roomCount,
    roomIndexBase,
    addressIndex,
    getRoom,
    mDirects,
    orphanSpaces,
    roomToParents,
    roomToUnread,
    myUserId,
  } = useRoomSearch({ onOpenRoomId: openRoomId });

  // Focus the input on mount. Inline form opens via an explicit user
  // action (chip tap or icon click), so this is request-initiated
  // focus rather than ambient `autoFocus` — keeps screen readers
  // happy.
  useEffect(() => {
    inputRef.current?.focus();
  }, [inputRef]);

  const isEmpty = roomsToRender.length === 0 && peopleToRender.length === 0 && !addressPreview;

  // Surfaced inside the confirm dialog (the only place a DM is created).
  let dmErrorText: string | undefined;
  if (dmError) {
    dmErrorText = dmError.rateLimited ? t('Search.dm_rate_limited') : t('Search.dm_failed');
  }

  // Built once, placed above the rooms (explicit address — primary
  // result) or below People (a bare-name compose suggestion).
  const addressCard = addressPreview
    ? (() => {
        const username = getMxIdLocalPart(addressPreview.userId) ?? addressPreview.userId;
        const server = getMxIdServer(addressPreview.userId);
        const header = (
          <Text size="L400" priority="300" style={{ padding: `${toRem(4)} ${toRem(10)}` }}>
            {t('Search.by_address')}
          </Text>
        );

        // Clean 404 → no such account: a non-clickable note, so you can't
        // start a chat (and a ghost room) with someone who doesn't exist.
        if (addressPreview.state === 'notfound') {
          return (
            <Box direction="Column" gap="100" style={{ padding: `${toRem(4)} 0` }}>
              {header}
              <Box alignItems="Center" gap="200" style={{ padding: `${toRem(4)} ${toRem(10)}` }}>
                <Icon size="100" src={Icons.Warning} style={{ color: color.Warning.Main }} />
                <Box direction="Column" style={{ minWidth: 0 }}>
                  <Text size="T300" style={{ color: color.Warning.Main }}>
                    {t('Search.user_not_found', { server })}
                  </Text>
                  <Text as="span" size="T200" priority="300" truncate>
                    {addressPreview.userId}
                  </Text>
                </Box>
              </Box>
            </Box>
          );
        }

        const focused = listFocus.index === addressIndex;
        const resolved = addressPreview.state === 'resolved';
        const name = addressPreview.displayName || username;
        const avatarSrc =
          resolved && addressPreview.avatarUrl
            ? mxcUrlToHttp(mx, addressPreview.avatarUrl, useAuthentication, 32, 32, 'crop') ??
              undefined
            : undefined;
        const caption = resolved ? undefined : t('Search.user_unreachable', { server });
        return (
          <Box direction="Column" gap="100" style={{ padding: `${toRem(4)} 0` }}>
            {header}
            <button
              type="button"
              data-focus-index={addressIndex}
              onClick={() =>
                requestDirect({
                  userId: addressPreview.userId,
                  displayName: addressPreview.displayName,
                  avatarUrl: addressPreview.avatarUrl,
                })
              }
              aria-pressed={focused}
              style={{
                ...ROW_BASE_STYLE,
                backgroundColor: focused ? color.Background.ContainerHover : 'transparent',
              }}
            >
              <Avatar size="200" radii="400">
                <UserAvatar
                  userId={addressPreview.userId}
                  src={avatarSrc}
                  alt={name}
                  renderFallback={() => (
                    <Text as="span" size="H6">
                      {nameInitials(name)}
                    </Text>
                  )}
                />
              </Avatar>
              <Box grow="Yes" alignItems="Center" gap="100" style={{ minWidth: 0 }}>
                <Text size="T400" truncate>
                  {name}
                </Text>
                <Text as="span" size="T200" priority="300" truncate>
                  @{username}
                </Text>
              </Box>
              {server && (
                <Box shrink="No">
                  <Text size="T200" priority="300" truncate>
                    <b>{server}</b>
                  </Text>
                </Box>
              )}
            </button>
            {caption && (
              <Text size="T200" priority="300" style={{ padding: `0 ${toRem(10)}` }}>
                {caption}
              </Text>
            )}
          </Box>
        );
      })()
    : null;

  return (
    <Box
      direction="Column"
      gap="200"
      // Fill the form's full outer height so the result list runs flush
      // into the curtain's top edge (the form snap drops the breather —
      // see `snapTopPx`). `- 8` accounts for `formInner`'s 8px top
      // padding (StreamHeader.css.ts), so measured `formInner.offsetHeight`
      // lands at exactly `SEARCH_FORM_BASE_PX`, keeping the curtain at its
      // resting top and the pre-measure fallback in lockstep.
      style={{ width: '100%', height: toRem(SEARCH_FORM_BASE_PX - 8) }}
    >
      {/* ── Input bar (h=48 / r=20 / pad 8/14 pill).
          `width: 100%` + `minWidth: 0` keep the bar a constant full
          width whether empty or typed — without it the flex row
          collapses to its content (the search icon) until text widens
          it, which read as the bar "growing as you type". */}
      <Box
        shrink="No"
        alignItems="Center"
        style={{
          width: '100%',
          minWidth: 0,
          backgroundColor: color.Background.Container,
          borderRadius: toRem(20),
          padding: `${toRem(8)} ${toRem(14)}`,
          height: toRem(48),
          gap: toRem(10),
        }}
      >
        <Icon size="50" src={Icons.Search} />
        <input
          ref={inputRef}
          type="text"
          onChange={handleInputChange}
          onKeyDown={handleInputKeyDown}
          placeholder={t('Search.search')}
          autoComplete="off"
          style={{
            flex: 1,
            appearance: 'none',
            border: 'none',
            outline: 'none',
            background: 'transparent',
            font: 'inherit',
            fontSize: toRem(14),
            color: color.Background.OnContainer,
            minWidth: 0,
          }}
        />
        {directoryLoading ? (
          <Spinner size="200" variant="Secondary" />
        ) : (
          query.length > 0 && (
            <IconButton
              type="button"
              size="300"
              radii="Pill"
              variant="SurfaceVariant"
              fill="None"
              onClick={clearSearch}
              aria-label={t('Search.clear')}
            >
              <Icon size="100" src={Icons.Cross} />
            </IconButton>
          )
        )}
      </Box>

      {/* ── Result list ────────────────────────────────────── */}
      <Box grow="Yes" style={{ minHeight: 0 }}>
        <Scroll ref={scrollRef} size="300" hideTrack visibility="Hover">
          {isEmpty && !directoryLoading && (
            <Box
              grow="Yes"
              alignItems="Center"
              justifyContent="Center"
              direction="Column"
              gap="100"
              style={{ paddingTop: toRem(40) }}
            >
              <Text size="H6" align="Center">
                {result ? t('Search.no_match_found') : t('Search.no_rooms')}
              </Text>
              <Text size="T200" align="Center" priority="300">
                {result
                  ? t('Search.no_match_for_query', { query: result.query })
                  : t('Search.no_rooms_to_display')}
              </Text>
              <Text
                size="T200"
                align="Center"
                priority="300"
                style={{ maxWidth: toRem(260), marginTop: toRem(8) }}
              >
                {t('Search.address_hint')}
              </Text>
            </Box>
          )}
          {/* Typed address → always the top result. */}
          {addressCard}
          {roomsToRender.length > 0 && (
            <Box direction="Column" gap="100" style={{ padding: `${toRem(4)} 0` }}>
              {roomsToRender.map((roomId, index) => {
                const room = getRoom(roomId);
                if (!room) return null;

                const dm = mDirects.has(roomId);
                const dmUserId = dm ? getDmUserId(roomId, getRoom, myUserId) : undefined;
                const dmUsername = dmUserId ? getMxIdLocalPart(dmUserId) : undefined;
                const dmUserServer = dmUserId ? getMxIdServer(dmUserId) : undefined;

                const allParents = getAllParents(roomToParents, roomId);
                const orphanParents = allParents
                  ? orphanSpaces.filter((o) => allParents.has(o))
                  : undefined;
                const perfectOrphanParent =
                  orphanParents && guessPerfectParent(mx, roomId, orphanParents);

                const exactParents = roomToParents.get(roomId);
                const perfectParent =
                  exactParents && guessPerfectParent(mx, roomId, Array.from(exactParents));

                const unread = roomToUnread.get(roomId);
                const focusIndex = roomIndexBase + index;
                const focused = listFocus.index === focusIndex;

                return (
                  <button
                    key={roomId}
                    type="button"
                    data-focus-index={focusIndex}
                    data-room-id={roomId}
                    data-space={room.isSpaceRoom()}
                    onClick={handleRoomClick}
                    aria-pressed={focused}
                    style={{
                      appearance: 'none',
                      WebkitAppearance: 'none',
                      border: 'none',
                      backgroundColor: focused ? color.Background.ContainerHover : 'transparent',
                      color: color.Background.OnContainer,
                      borderRadius: toRem(12),
                      padding: `${toRem(8)} ${toRem(10)}`,
                      width: '100%',
                      display: 'flex',
                      alignItems: 'center',
                      gap: toRem(10),
                      cursor: 'pointer',
                      textAlign: 'left',
                      font: 'inherit',
                    }}
                  >
                    <Avatar size="200" radii={dm ? '400' : '300'}>
                      {dm || room.isSpaceRoom() ? (
                        <RoomAvatar
                          roomId={room.roomId}
                          src={
                            dm
                              ? getDirectRoomAvatarUrl(mx, room, 32, useAuthentication)
                              : getRoomAvatarUrl(mx, room, 32, useAuthentication)
                          }
                          alt={room.name}
                          renderFallback={() => (
                            <Text as="span" size="H6">
                              {nameInitials(room.name)}
                            </Text>
                          )}
                        />
                      ) : (
                        <RoomIcon
                          size="100"
                          joinRule={room.getJoinRule()}
                          roomType={room.getType()}
                        />
                      )}
                    </Avatar>
                    <Box grow="Yes" alignItems="Center" gap="100" style={{ minWidth: 0 }}>
                      <Text size="T400" truncate>
                        {room.name}
                      </Text>
                      {dmUsername && (
                        <Text as="span" size="T200" priority="300" truncate>
                          @{dmUsername}
                        </Text>
                      )}
                      {!dm && perfectParent && perfectParent !== perfectOrphanParent && (
                        <Text size="T200" priority="300" truncate>
                          — {getRoom(perfectParent)?.name ?? perfectParent}
                        </Text>
                      )}
                    </Box>
                    <Box gap="100" alignItems="Center" shrink="No">
                      {dmUserServer && (
                        <Text size="T200" priority="300" truncate>
                          <b>{dmUserServer}</b>
                        </Text>
                      )}
                      {!dm && perfectOrphanParent && (
                        <Text size="T200" priority="300" truncate>
                          <b>{getRoom(perfectOrphanParent)?.name ?? perfectOrphanParent}</b>
                        </Text>
                      )}
                      {unread && (
                        <UnreadBadgeCenter>
                          <UnreadBadge highlight={unread.highlight > 0} count={unread.total} />
                        </UnreadBadgeCenter>
                      )}
                    </Box>
                  </button>
                );
              })}
            </Box>
          )}

          {/* ── People (homeserver user directory) ─────────────── */}
          {peopleToRender.length > 0 && (
            <Box direction="Column" gap="100" style={{ padding: `${toRem(4)} 0` }}>
              <Text size="L400" priority="300" style={{ padding: `${toRem(4)} ${toRem(10)}` }}>
                {t('Search.people')}
              </Text>
              {peopleToRender.map((user, j) => {
                const index = roomIndexBase + roomCount + j;
                const focused = listFocus.index === index;
                const username = getMxIdLocalPart(user.userId) ?? user.userId;
                const server = getMxIdServer(user.userId);
                const name = user.displayName || username;
                const avatarSrc = user.avatarUrl
                  ? mxcUrlToHttp(mx, user.avatarUrl, useAuthentication, 32, 32, 'crop') ?? undefined
                  : undefined;

                return (
                  <button
                    key={user.userId}
                    type="button"
                    data-focus-index={index}
                    onClick={() =>
                      requestDirect({
                        userId: user.userId,
                        displayName: user.displayName,
                        avatarUrl: user.avatarUrl,
                      })
                    }
                    aria-pressed={focused}
                    style={{
                      ...ROW_BASE_STYLE,
                      backgroundColor: focused ? color.Background.ContainerHover : 'transparent',
                    }}
                  >
                    <Avatar size="200" radii="400">
                      <UserAvatar
                        userId={user.userId}
                        src={avatarSrc}
                        alt={name}
                        renderFallback={() => (
                          <Text as="span" size="H6">
                            {nameInitials(name)}
                          </Text>
                        )}
                      />
                    </Avatar>
                    <Box grow="Yes" alignItems="Center" gap="100" style={{ minWidth: 0 }}>
                      <Text size="T400" truncate>
                        {name}
                      </Text>
                      <Text as="span" size="T200" priority="300" truncate>
                        @{username}
                      </Text>
                    </Box>
                    {server && (
                      <Box shrink="No">
                        <Text size="T200" priority="300" truncate>
                          <b>{server}</b>
                        </Text>
                      </Box>
                    )}
                  </button>
                );
              })}
            </Box>
          )}
        </Scroll>
      </Box>

      {pendingDirect && (
        <StartDirectDialog
          key={pendingDirect.userId}
          target={pendingDirect}
          creating={creatingUserId === pendingDirect.userId}
          errorText={dmErrorText}
          onConfirm={confirmDirect}
          onCancel={cancelDirect}
        />
      )}
    </Box>
  );
}
