import React, { useMemo, useState, useCallback, useRef, useEffect, MouseEvent } from 'react';
import FocusTrap from 'focus-trap-react';
import { Dialog, Overlay, OverlayCenter, OverlayBackdrop, Header, config, Box, Text, IconButton, color, Button, Spinner, Scroll, Checkbox, Badge, Line, Avatar } from 'folds';
import { Icon, Icons } from '../icons';
import { Room } from 'matrix-js-sdk';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { stopPropagation } from '../../utils/keyboard';
import { RoomAvatar } from '../room-avatar';
import { nameInitials } from '../../utils/common';
import { getMxIdLocalPart } from '../../utils/matrix';
import { getRoomAvatarUrl } from '../../utils/room';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';

const BASE_DELAY_MS = 1000;
const MAX_RETRIES = 3;

/**
 * Delays execution for a specified time
 */
function delay(ms: number): Promise<void> {
  return new Promise<void>((resolve) => {
    setTimeout(resolve, ms);
  });
}

/**
 * Gets the retry delay from a rate limit error, or returns a default backoff
 */
function getRetryDelay(error: unknown, attempt: number): number {
  // Check for Matrix rate limit error with retry_after_ms
  if (
    error &&
    typeof error === 'object' &&
    'data' in error &&
    error.data &&
    typeof error.data === 'object' &&
    'retry_after_ms' in error.data &&
    typeof error.data.retry_after_ms === 'number'
  ) {
    return error.data.retry_after_ms + 100; // Add small buffer
  }
  // Exponential backoff: 2s, 4s, 8s...
  return Math.min(2000 * 2 ** attempt, 30000);
}

type RoomItemProps = {
  room: Room;
  selected: boolean;
  onToggle: () => void;
  onShiftClick: () => void;
  disabled?: boolean;
};

/**
 * Individual room item component with checkbox
 */
function RoomItem({ room, selected, onToggle, onShiftClick, disabled }: RoomItemProps) {
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const avatarUrl = getRoomAvatarUrl(mx, room, 32, useAuthentication);
  const roomName = room.name || getMxIdLocalPart(room.roomId) || room.roomId;
  const memberCount = room.getJoinedMemberCount();

  const handleClick = (evt: MouseEvent<HTMLLabelElement>) => {
    if (disabled) return;
    if (evt.shiftKey) {
      evt.preventDefault();
      onShiftClick();
    }
  };

  return (
    <Box
      as="label"
      gap="300"
      alignItems="Center"
      onClick={handleClick}
      style={{
        padding: config.space.S200,
        borderRadius: config.radii.R300,
        cursor: disabled ? 'not-allowed' : 'pointer',
        opacity: disabled ? 0.6 : 1,
        background: selected ? 'var(--bg-surface-active)' : undefined,
      }}
    >
      <Checkbox
        size="300"
        variant="Primary"
        checked={selected}
        onClick={disabled ? undefined : onToggle}
        disabled={disabled}
      />
      <Avatar size="200">
        <RoomAvatar
          roomId={room.roomId}
          src={avatarUrl}
          alt={roomName}
          renderFallback={() => (
            <Text size="H6">{nameInitials(roomName, 2)}</Text>
          )}
        />
      </Avatar>
      <Box direction="Column" grow="Yes" style={{ minWidth: 0 }}>
        <Text size="T300" truncate>
          {roomName}
        </Text>
        <Text size="T200" style={{ opacity: 0.7 }} truncate>
          {memberCount} {memberCount === 1 ? 'member' : 'members'}
        </Text>
      </Box>
    </Box>
  );
}

type LeavePhase = 'selecting' | 'leaving' | 'done';

type ManageRoomsPromptProps = {
  rooms: Room[];
  onDone: () => void;
  onCancel: () => void;
};

/**
 * Dialog for managing multiple rooms with multi-select and mass leave functionality
 */
export function ManageRoomsPrompt({ rooms, onDone, onCancel }: ManageRoomsPromptProps) {
  const mx = useMatrixClient();
  const [selectedRoomIds, setSelectedRoomIds] = useState<Set<string>>(new Set());
  const [phase, setPhase] = useState<LeavePhase>('selecting');
  const [leaveProgress, setLeaveProgress] = useState<{ current: number; total: number } | null>(
    null
  );
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const lastSelectedIndex = useRef<number | null>(null);

  const sortedRooms = useMemo(
    () =>
      [...rooms].sort((a, b) => {
        const nameA = a.name?.toLowerCase() || '';
        const nameB = b.name?.toLowerCase() || '';
        return nameA.localeCompare(nameB);
      }),
    [rooms]
  );

  /**
   * Handles toggling individual room selection
   */
  const handleToggle = useCallback((roomId: string, index: number) => {
    setSelectedRoomIds((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(roomId)) {
        newSet.delete(roomId);
      } else {
        newSet.add(roomId);
      }
      return newSet;
    });
    lastSelectedIndex.current = index;
  }, []);

  /**
   * Handles shift-click for range selection
   */
  const handleShiftClick = useCallback(
    (roomId: string, index: number) => {
      if (lastSelectedIndex.current === null) {
        handleToggle(roomId, index);
        return;
      }

      const start = Math.min(lastSelectedIndex.current, index);
      const end = Math.max(lastSelectedIndex.current, index);

      setSelectedRoomIds((prev) => {
        const newSet = new Set(prev);
        sortedRooms.slice(start, end + 1).forEach((r) => newSet.add(r.roomId));
        return newSet;
      });
      lastSelectedIndex.current = index;
    },
    [handleToggle, sortedRooms]
  );

  /**
   * Select all rooms
   */
  const handleSelectAll = useCallback(() => {
    setSelectedRoomIds(new Set(sortedRooms.map((r) => r.roomId)));
  }, [sortedRooms]);

  /**
   * Deselect all rooms
   */
  const handleDeselectAll = useCallback(() => {
    setSelectedRoomIds(new Set());
    lastSelectedIndex.current = null;
  }, []);

  /**
   * Leaves a single room with retry on rate limit
   */
  const leaveRoom = useCallback(
    async (roomId: string, attempt = 0): Promise<boolean> => {
      try {
        await mx.leave(roomId);
        return true;
      } catch (error) {
        // Check if it's a rate limit error (429)
        const isRateLimited =
          error &&
          typeof error === 'object' &&
          'httpStatus' in error &&
          error.httpStatus === 429;

        if (isRateLimited && attempt < MAX_RETRIES) {
          const retryDelay = getRetryDelay(error, attempt);
          await delay(retryDelay);
          return leaveRoom(roomId, attempt + 1);
        }
        return false;
      }
    },
    [mx]
  );

  /**
   * Performs mass leave operation
   */
  const handleMassLeave = useCallback(async () => {
    if (selectedRoomIds.size === 0) return;

    setPhase('leaving');
    setErrorMessage(null);
    const roomsToLeave = Array.from(selectedRoomIds);
    const total = roomsToLeave.length;
    let failedCount = 0;

    // Process rooms sequentially with rate limiting
    const processRoom = async (index: number): Promise<void> => {
      if (index >= roomsToLeave.length) {
        setPhase('done');
        if (failedCount > 0) {
          setErrorMessage(`Failed to leave ${failedCount} room(s).`);
        }
        return;
      }

      const roomId = roomsToLeave[index];
      setLeaveProgress({ current: index + 1, total });

      const success = await leaveRoom(roomId);
      if (!success) {
        failedCount += 1;
      }

      // Rate limiting delay before next room
      if (index < roomsToLeave.length - 1) {
        await delay(BASE_DELAY_MS);
      }

      await processRoom(index + 1);
    };

    await processRoom(0);
  }, [leaveRoom, selectedRoomIds]);

  useEffect(() => {
    if (phase === 'done' && !errorMessage) {
      // Auto close on success after short delay
      const timer = setTimeout(onDone, 500);
      return () => clearTimeout(timer);
    }
    return undefined;
  }, [phase, errorMessage, onDone]);

  const renderContent = () => {
    if (phase === 'leaving') {
      return (
        <Box
          style={{ padding: config.space.S400, minHeight: '200px' }}
          direction="Column"
          alignItems="Center"
          justifyContent="Center"
          gap="400"
        >
          <Spinner size="600" variant="Secondary" />
          <Text>
            Leaving rooms... {leaveProgress?.current} / {leaveProgress?.total}
          </Text>
        </Box>
      );
    }

    if (phase === 'done') {
      return (
        <Box
          style={{ padding: config.space.S400, minHeight: '200px' }}
          direction="Column"
          alignItems="Center"
          justifyContent="Center"
          gap="400"
        >
          {errorMessage ? (
            <>
              <Icon src={Icons.Warning} size="600" style={{ color: color.Warning.Main }} />
              <Text style={{ color: color.Warning.Main }}>{errorMessage}</Text>
              <Button variant="Secondary" onClick={onDone}>
                <Text size="B300">Close</Text>
              </Button>
            </>
          ) : (
            <>
              <Icon src={Icons.Check} size="600" style={{ color: color.Success.Main }} />
              <Text style={{ color: color.Success.Main }}>Successfully left all selected rooms!</Text>
            </>
          )}
        </Box>
      );
    }

    // Selection phase
    return (
      <>
        <Box style={{ padding: config.space.S400 }} direction="Column" gap="300">
          <Box justifyContent="SpaceBetween" alignItems="Center">
            <Text size="T300" style={{ opacity: 0.8 }}>
              {selectedRoomIds.size} of {sortedRooms.length} rooms selected
            </Text>
            <Box gap="200">
              <Button variant="Secondary" size="300" onClick={handleSelectAll}>
                <Text size="B300">Select All</Text>
              </Button>
              <Button
                variant="Secondary"
                size="300"
                onClick={handleDeselectAll}
                disabled={selectedRoomIds.size === 0}
              >
                <Text size="B300">Deselect All</Text>
              </Button>
            </Box>
          </Box>
          <Text size="T200" style={{ opacity: 0.6 }}>
            Tip: Hold Shift and click to select a range of rooms
          </Text>
        </Box>
        <Line variant="Surface" size="300" />
        <Box style={{ flexGrow: 1, minHeight: 0 }}>
          <Scroll hideTrack visibility="Hover" style={{ maxHeight: '400px' }}>
            <Box direction="Column" gap="100" style={{ padding: config.space.S200 }}>
              {sortedRooms.map((room, index) => (
                <RoomItem
                  key={room.roomId}
                  room={room}
                  selected={selectedRoomIds.has(room.roomId)}
                  onToggle={() => handleToggle(room.roomId, index)}
                  onShiftClick={() => handleShiftClick(room.roomId, index)}
                />
              ))}
            </Box>
          </Scroll>
        </Box>
        <Line variant="Surface" size="300" />
        <Box
          style={{ padding: config.space.S400 }}
          justifyContent="SpaceBetween"
          alignItems="Center"
          gap="300"
        >
          <Button variant="Secondary" fill="Soft" onClick={onCancel}>
            <Text size="B300">Cancel</Text>
          </Button>
          <Button
            variant="Critical"
            onClick={handleMassLeave}
            disabled={selectedRoomIds.size === 0}
          >
            <Icon src={Icons.ArrowGoRight} size="200" />
            <Text size="B300">Leave Selected ({selectedRoomIds.size})</Text>
          </Button>
        </Box>
      </>
    );
  };

  return (
    <Overlay open backdrop={<OverlayBackdrop />}>
      <OverlayCenter>
        <FocusTrap
          focusTrapOptions={{
            initialFocus: false,
            onDeactivate: phase === 'selecting' ? onCancel : undefined,
            clickOutsideDeactivates: phase === 'selecting',
            escapeDeactivates: phase === 'selecting' ? stopPropagation : false,
          }}
        >
          <Dialog variant="Surface" style={{ width: '500px', maxWidth: '90vw' }}>
            <Header
              style={{
                padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
                borderBottomWidth: config.borderWidth.B300,
              }}
              variant="Surface"
              size="500"
            >
              <Box grow="Yes" alignItems="Center" gap="200">
                <Text size="H4">Manage Rooms</Text>
                {selectedRoomIds.size > 0 && (
                  <Badge variant="Secondary" radii="Pill">
                    <Text size="L400">{selectedRoomIds.size}</Text>
                  </Badge>
                )}
              </Box>
              {phase === 'selecting' && (
                <IconButton size="300" onClick={onCancel} radii="300">
                  <Icon src={Icons.Cross} />
                </IconButton>
              )}
            </Header>
            {renderContent()}
          </Dialog>
        </FocusTrap>
      </OverlayCenter>
    </Overlay>
  );
}
