import React, { useCallback } from 'react';
import { MatrixError, Room as MatrixRoom } from 'matrix-js-sdk';
import { useTranslation } from 'react-i18next';
import { Box, Button, Icon, Icons, Spinner, Text, color } from 'folds';
import type { BotPreset } from '../../../features/bots/catalog';
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { removeRoomIdFromMDirect } from '../../../utils/matrix';
import { BotOpenChatAction } from './BotOpenChatAction';
import { BotStatePage } from './BotStatePage';

type BotInvitePendingProps = {
  preset: BotPreset;
  room: MatrixRoom;
  selfInvite?: boolean;
};

export function BotInvitePending({ preset, room, selfInvite }: BotInvitePendingProps) {
  const { t } = useTranslation();
  const mx = useMatrixClient();

  // Self-invite: user has been invited to the control DM (e.g. from another
  // device or by the bot itself). Accept inline so the user doesn't have to
  // bounce through /direct/ to continue.
  const [acceptState, accept] = useAsyncCallback<void, Error | MatrixError, []>(
    useCallback(async () => {
      await mx.joinRoom(room.roomId);
    }, [mx, room])
  );

  // Self-invite decline: leave the invited control DM and drop its m.direct
  // pointer. We intentionally do not expose this reset while waiting for the
  // bot to join; creating a replacement room there can race with the bridge
  // accepting the original invite and recreate the duplicate-DM bug.
  const [resetState, reset] = useAsyncCallback<void, Error | MatrixError, []>(
    useCallback(async () => {
      await mx.leave(room.roomId).catch(() => undefined);
      await removeRoomIdFromMDirect(mx, room.roomId);
      mx.forget(room.roomId).catch(() => undefined);
    }, [mx, room])
  );

  const acceptLoading = acceptState.status === AsyncStatus.Loading;
  const acceptError = acceptState.status === AsyncStatus.Error ? acceptState.error : undefined;
  const resetLoading = resetState.status === AsyncStatus.Loading;
  const resetError =
    selfInvite && resetState.status === AsyncStatus.Error ? resetState.error : undefined;
  const error = acceptError ?? resetError;

  return (
    <BotStatePage
      icon={<Icon size="600" src={Icons.Info} />}
      title={t('Bots.pending_title', { name: preset.name })}
      description={
        selfInvite
          ? t('Bots.pending_self_invite_description')
          : t('Bots.pending_bot_invite_description', { mxid: preset.mxid })
      }
    >
      {error && (
        <Box style={{ color: color.Critical.Main }} alignItems="Center" gap="200">
          <Icon src={Icons.Warning} filled size="100" />
          <Text size="T300" style={{ color: color.Critical.Main }}>
            <b>{error.message || (acceptError ? t('Bots.accept_error') : t('Bots.reset_error'))}</b>
          </Text>
        </Box>
      )}
      {selfInvite && (
        <Button
          variant="Primary"
          size="500"
          radii="400"
          onClick={() => accept().catch(() => undefined)}
          disabled={acceptLoading || resetLoading}
          before={acceptLoading && <Spinner variant="Primary" fill="Solid" size="200" />}
        >
          <Text size="B500">{t('Bots.accept_invite')}</Text>
        </Button>
      )}
      {selfInvite && (
        <Button
          variant="Secondary"
          fill="Soft"
          size="500"
          radii="400"
          onClick={() => reset().catch(() => undefined)}
          disabled={acceptLoading || resetLoading}
          before={resetLoading && <Spinner variant="Secondary" fill="Soft" size="200" />}
        >
          <Text size="B500">{t('Bots.decline_invite')}</Text>
        </Button>
      )}
      <BotOpenChatAction room={room} />
    </BotStatePage>
  );
}
