import React, { useCallback } from 'react';
import {
  Avatar,
  Badge,
  Box,
  Button,
  Icon,
  IconButton,
  Icons,
  Scroll,
  Spinner,
  Text,
  color,
  toRem,
} from 'folds';
import { useAtomValue } from 'jotai';
import { MatrixError, Room } from 'matrix-js-sdk';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import * as css from './JoinBeforeNavigate.css';
import { RoomAvatar, RoomIcon } from '../../components/room-avatar';
import { Page, PageHeader } from '../../components/page';
import { RoomSummaryLoader } from '../../components/RoomSummaryLoader';
import { useRoomNavigate } from '../../hooks/useRoomNavigate';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { useJoinedRoomId } from '../../hooks/useJoinedRoomId';
import { allRoomsAtom } from '../../state/room-list/roomList';
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { BackRouteHandler } from '../../components/BackRouteHandler';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
import { getRoomAvatarUrl } from '../../utils/room';
import { RoomType } from '../../../types/matrix/room';
import { millify } from '../../plugins/millify';

type JoinBeforeNavigateProps = { roomIdOrAlias: string; eventId?: string; viaServers?: string[] };
export function JoinBeforeNavigate({
  roomIdOrAlias,
  eventId,
  viaServers,
}: JoinBeforeNavigateProps) {
  const { t } = useTranslation();
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const allRooms = useAtomValue(allRoomsAtom);
  const { navigateRoom, navigateSpace } = useRoomNavigate();
  const navigate = useNavigate();
  const screenSize = useScreenSizeContext();

  const joinedRoomId = useJoinedRoomId(allRooms, roomIdOrAlias);
  // For a pending invite the room already exists locally (membership "invite"),
  // so resolve it for the richer avatar / member count / membership signal.
  const room = mx.getRoom(joinedRoomId ?? roomIdOrAlias) ?? undefined;
  const invited = room?.getMyMembership() === 'invite';

  const handleView = (roomId: string) => {
    if (mx.getRoom(roomId)?.isSpaceRoom()) {
      navigateSpace(roomId);
      return;
    }
    navigateRoom(roomId, eventId);
  };

  const [joinState, join] = useAsyncCallback<Room, MatrixError, []>(
    useCallback(() => mx.joinRoom(roomIdOrAlias, { viaServers }), [mx, roomIdOrAlias, viaServers])
  );
  const joining =
    joinState.status === AsyncStatus.Loading || joinState.status === AsyncStatus.Success;

  const [declineState, decline] = useAsyncCallback<void, MatrixError, []>(
    useCallback(async () => {
      if (room) await mx.leave(room.roomId);
      navigate('/');
    }, [mx, room, navigate])
  );
  const declining =
    declineState.status === AsyncStatus.Loading || declineState.status === AsyncStatus.Success;

  return (
    <Page>
      <PageHeader balance>
        <Box grow="Yes" gap="200">
          <Box shrink="No">
            {screenSize === ScreenSize.Mobile && (
              <BackRouteHandler>
                {(onBack) => (
                  <IconButton onClick={onBack}>
                    <Icon src={Icons.ArrowLeft} />
                  </IconButton>
                )}
              </BackRouteHandler>
            )}
          </Box>
          <Box grow="Yes" justifyContent="Center" alignItems="Center" gap="200">
            <Text size="H4" truncate>
              {room?.name ?? t('Explore.join')}
            </Text>
          </Box>
        </Box>
      </PageHeader>
      <Box grow="Yes">
        <Scroll hideTrack visibility="Hover" size="0">
          <RoomSummaryLoader roomIdOrAlias={roomIdOrAlias}>
            {(summary) => {
              const avatarUrl = room
                ? getRoomAvatarUrl(mx, room, 96, useAuthentication)
                : (summary?.avatar_url &&
                    mxcUrlToHttp(mx, summary.avatar_url, useAuthentication, 96, 96, 'crop')) ||
                  undefined;
              const name =
                room?.name ?? summary?.name ?? getMxIdLocalPart(roomIdOrAlias) ?? roomIdOrAlias;
              const topic = summary?.topic;
              const memberCount = room?.getJoinedMemberCount() ?? summary?.num_joined_members;
              const isSpace = room?.isSpaceRoom() || summary?.room_type === RoomType.Space;
              const errored = joinState.status === AsyncStatus.Error;

              let primaryLabel = t('Explore.join');
              if (joining) primaryLabel = t('Explore.joining');
              else if (invited) primaryLabel = t('Direct.invite_accept');

              return (
                <div className={css.Hero}>
                  <Avatar size="500" radii="500" style={{ width: toRem(84), height: toRem(84) }}>
                    <RoomAvatar
                      roomId={room?.roomId ?? roomIdOrAlias}
                      src={avatarUrl ?? undefined}
                      alt={name}
                      renderFallback={() => (
                        <RoomIcon
                          size="600"
                          roomType={isSpace ? RoomType.Space : undefined}
                          filled
                        />
                      )}
                    />
                  </Avatar>

                  {invited && <span className={css.Eyebrow}>{t('Room.invite')}</span>}

                  <Box direction="Column" gap="200" alignItems="Center">
                    <Text size="H3" align="Center">
                      {name}
                    </Text>
                    {isSpace && (
                      <Badge variant="Secondary" fill="Soft" outlined>
                        <Text size="L400">{t('Explore.space_badge')}</Text>
                      </Badge>
                    )}
                    {typeof memberCount === 'number' && (
                      <Box gap="100" alignItems="Center">
                        <Icon size="50" src={Icons.User} />
                        <Text size="T200" as="span" className={css.MemberCount}>
                          {t('Explore.members_count', {
                            count: memberCount,
                            formattedCount: millify(memberCount),
                          })}
                        </Text>
                      </Box>
                    )}
                  </Box>

                  {topic && (
                    <Text size="T300" className={css.Topic} align="Center">
                      {topic}
                    </Text>
                  )}

                  <div className={css.Actions}>
                    {typeof joinedRoomId === 'string' ? (
                      <Button onClick={() => handleView(joinedRoomId)} variant="Primary" size="500">
                        <Text size="B500">{t('Explore.view')}</Text>
                      </Button>
                    ) : (
                      <Button
                        onClick={join}
                        variant="Primary"
                        size="500"
                        disabled={joining}
                        before={joining && <Spinner size="200" variant="Primary" fill="Solid" />}
                      >
                        <Text size="B500">{primaryLabel}</Text>
                      </Button>
                    )}

                    {invited && typeof joinedRoomId !== 'string' && (
                      <Button
                        onClick={decline}
                        variant="Secondary"
                        fill="Soft"
                        size="500"
                        disabled={declining}
                        before={declining && <Spinner size="200" variant="Secondary" />}
                      >
                        <Text size="B500">{t('Direct.invite_decline')}</Text>
                      </Button>
                    )}

                    {errored && (
                      <Text size="T200" align="Center" style={{ color: color.Critical.Main }}>
                        {joinState.error.message || t('Explore.join_error_unknown')}
                      </Text>
                    )}
                  </div>
                </div>
              );
            }}
          </RoomSummaryLoader>
        </Scroll>
      </Box>
    </Page>
  );
}
