import React, { useCallback } from 'react';
import { Room } from 'matrix-js-sdk/lib/models/room';
import { MatrixError } from 'matrix-js-sdk';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { Avatar, Badge, Box, Button, Spinner, Text, color, toRem } from 'folds';
import { NavItem, NavItemContent } from '../../components/nav';
import { RoomAvatar } from '../../components/room-avatar';
import { nameInitials } from '../../utils/common';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
import {
  getDirectRoomAvatarUrl,
  getMemberDisplayName,
  getRoomAvatarUrl,
  getStateEvent,
  isDirectInvite,
  isSpace,
} from '../../utils/room';
import {
  addRoomIdToMDirect,
  getCanonicalAliasOrRoomId,
  getMxIdLocalPart,
  guessDmRoomUserId,
} from '../../utils/matrix';
import { getDirectRoomPath } from '../../pages/pathUtils';
import { StateEvent } from '../../../types/matrix/room';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';

const ROW_MIN_HEIGHT = toRem(68);

type DirectInviteRowProps = {
  room: Room;
  selected: boolean;
  isSpam?: boolean;
};

export function DirectInviteRow({ room, selected, isSpam }: DirectInviteRowProps) {
  const { t } = useTranslation();
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const { navigateSpace } = useRoomNavigate();
  const navigate = useNavigate();
  const userId = mx.getSafeUserId();

  const direct = isDirectInvite(room, userId);
  const space = isSpace(room);
  const encrypted = !!getStateEvent(room, StateEvent.RoomEncryption);

  const avatar = direct
    ? getDirectRoomAvatarUrl(mx, room, 96, useAuthentication)
    : getRoomAvatarUrl(mx, room, 96, useAuthentication);
  const roomName = room.name || room.getCanonicalAlias() || room.roomId;

  const member = room.getMember(userId);
  const senderId = member?.events.member?.getSender();
  let senderName: string | null = null;
  if (senderId) {
    senderName = getMemberDisplayName(room, senderId) ?? getMxIdLocalPart(senderId) ?? senderId;
  }
  const topic =
    getStateEvent(room, StateEvent.RoomTopic)?.getContent<{ topic?: string }>()?.topic ?? null;
  let kindLabel: string;
  if (space) {
    kindLabel = t('Direct.invite_kind_space');
  } else if (direct) {
    kindLabel = t('Direct.invite_kind_direct');
  } else {
    kindLabel = t('Direct.invite_kind_group');
  }

  const [joinState, join] = useAsyncCallback<void, MatrixError, []>(
    useCallback(async () => {
      // m.direct is also written by the global useAutoDirectSync listener on
      // the invite→join MyMembership transition; the call here lets the
      // navigate() below land on a route that already sees the room as a DM,
      // shaving one render. Both writes are idempotent.
      const dmUserId = direct ? guessDmRoomUserId(room, userId) : undefined;
      await mx.joinRoom(room.roomId);
      if (dmUserId) {
        await addRoomIdToMDirect(mx, room.roomId, dmUserId);
      }
      if (space) {
        navigateSpace(room.roomId);
        return;
      }
      const alias = getCanonicalAliasOrRoomId(mx, room.roomId);
      navigate(getDirectRoomPath(alias));
    }, [mx, room, direct, space, userId, navigate, navigateSpace])
  );

  const [leaveState, leave] = useAsyncCallback<Record<string, never>, MatrixError, []>(
    useCallback(() => mx.leave(room.roomId), [mx, room.roomId])
  );

  const joining =
    joinState.status === AsyncStatus.Loading || joinState.status === AsyncStatus.Success;
  const leaving =
    leaveState.status === AsyncStatus.Loading || leaveState.status === AsyncStatus.Success;
  const busy = joining || leaving;

  let errMessage: string | null = null;
  if (joinState.status === AsyncStatus.Error) {
    errMessage = joinState.error.message;
  } else if (leaveState.status === AsyncStatus.Error) {
    errMessage = leaveState.error.message;
  }

  const inviteLine = senderName
    ? t('Direct.invite_from_with_kind', { sender: senderName, kind: kindLabel })
    : t('Direct.invite_to_kind', { kind: kindLabel });

  let kindBadge: React.ReactNode;
  if (space) {
    kindBadge = (
      <Badge variant="Secondary" fill="Soft" radii="300" size="400">
        <Text as="span" size="L400">
          {t('Direct.invite_badge_space')}
        </Text>
      </Badge>
    );
  } else if (direct) {
    kindBadge = (
      <Badge variant="Primary" fill="Solid" radii="300" size="400">
        <Text as="span" size="L400">
          {t('Direct.invite_badge_direct')}
        </Text>
      </Badge>
    );
  } else {
    kindBadge = (
      <Badge variant="Secondary" fill="Solid" radii="300" size="400">
        <Text as="span" size="L400">
          {t('Direct.invite_badge_group')}
        </Text>
      </Badge>
    );
  }

  const badgeRow = (
    <Box as="span" alignItems="Center" gap="100" wrap="Wrap">
      {kindBadge}
      {encrypted && (
        <Badge variant="Success" fill="Solid" radii="300" size="400">
          <Text as="span" size="L400">
            {t('Direct.invite_badge_encrypted')}
          </Text>
        </Badge>
      )}
      {isSpam && (
        <Badge variant="Critical" fill="Solid" radii="300" size="400">
          <Text as="span" size="L400">
            {t('Direct.invite_badge_spam')}
          </Text>
        </Badge>
      )}
    </Box>
  );

  return (
    <NavItem
      variant="Background"
      radii="400"
      highlight={!isSpam}
      aria-selected={selected}
      style={{
        minHeight: ROW_MIN_HEIGHT,
        boxSizing: 'border-box',
        opacity: isSpam ? 0.7 : 1,
      }}
    >
      <NavItemContent>
        <Box
          as="span"
          grow="Yes"
          alignItems="Start"
          gap="300"
          style={{
            minHeight: ROW_MIN_HEIGHT,
            boxSizing: 'border-box',
            padding: `${toRem(8)} 0`,
          }}
        >
          <Avatar size="300" radii="400">
            <RoomAvatar
              roomId={room.roomId}
              src={isSpam ? undefined : avatar}
              alt={roomName}
              renderFallback={() => (
                <Text as="span" size="H6">
                  {nameInitials(isSpam ? undefined : roomName)}
                </Text>
              )}
            />
          </Avatar>
          <Box
            as="span"
            direction="Column"
            grow="Yes"
            gap="100"
            style={{ minWidth: 0, overflow: 'hidden' }}
          >
            <Box as="span" alignItems="Center" gap="200" style={{ minWidth: 0 }}>
              <Box
                as="span"
                grow="Yes"
                style={{ minWidth: 0, overflow: 'hidden' }}
              >
                <Text as="span" size="T300" truncate style={{ fontWeight: 600 }}>
                  {roomName}
                </Text>
              </Box>
              <Box as="span" shrink="No">
                {badgeRow}
              </Box>
            </Box>
            <Text
              as="span"
              size="T200"
              truncate
              style={{
                opacity: 0.7,
                fontWeight: 400,
              }}
            >
              {inviteLine}
            </Text>
            {topic && (
              <Text
                as="span"
                size="T200"
                truncate
                style={{
                  opacity: 0.55,
                  fontWeight: 400,
                  fontStyle: 'italic',
                }}
              >
                {topic}
              </Text>
            )}
            {errMessage && (
              <Text as="span" size="T200" style={{ color: color.Critical.Main }}>
                {errMessage}
              </Text>
            )}
            <Box as="span" alignItems="Center" gap="200" style={{ marginTop: toRem(4) }}>
              <Button
                size="300"
                variant="Secondary"
                fill="Soft"
                radii="300"
                disabled={busy}
                onClick={leave}
                before={leaving ? <Spinner variant="Secondary" size="100" /> : undefined}
              >
                <Text as="span" size="B300">
                  {t('Direct.invite_decline')}
                </Text>
              </Button>
              <Button
                size="300"
                variant="Success"
                fill="Soft"
                radii="300"
                outlined
                disabled={busy}
                onClick={join}
                before={joining ? <Spinner variant="Success" fill="Soft" size="100" /> : undefined}
              >
                <Text as="span" size="B300">
                  {t('Direct.invite_accept')}
                </Text>
              </Button>
            </Box>
          </Box>
        </Box>
      </NavItemContent>
    </NavItem>
  );
}
