import { useCallback, useMemo, useRef } from 'react';

import SelectableListItem from '@composed-components/SelectableListItem';
import UserAvatar from '@composed-components/UserAvatar';
import Badge from '@design-system/Badge';
import CardContainer from '@design-system/CardContainer';
import Search from '@design-system/Inputs/Search';
import List from '@design-system/List';
import StateCard from '@design-system/StateCard';
import Title from '@design-system/Title';
import useVirtualizer from '@hooks/useVirtualizer';
import Button from '@mui/lab/LoadingButton';
import { CircularProgress, Typography } from '@mui/material';
import Stack from '@mui/material/Stack';
import { IconFilter } from '@tabler/icons-react';
import { appearFromBottom } from '@utils/animations';

import SkeletonList from './components/SkeletonList';
import { type IndividualSelectionProps } from './types';

const ROW_HEIGHT = 64;
const ITEM_SX = { borderRadius: 1 };

const IndividualSelection = ({
  filterCount,
  onChange,
  onSearch,
  onSelectAll,
  searchValue = '',
  slotProps,
  usersQuery,
  usersQueryDataParser,
  selectionLimit,
  value = new Set(),
  idKey = 'id',
  sx,
}: IndividualSelectionProps) => {
  const scrollRef = useRef<HTMLDivElement>(null);

  const parsedData = useMemo(
    () => usersQueryDataParser(usersQuery.data),
    [usersQuery.data, usersQueryDataParser],
  );

  const selectedAll = useMemo(
    () =>
      !!parsedData?.length &&
      parsedData.every(user =>
        value?.has(user[idKey as keyof typeof user] as string),
      ),
    [parsedData, value, idKey],
  );

  const valueRef = useRef(value);
  valueRef.current = value;

  const handleUserSelect = useCallback(
    (userId: string) => {
      const newSet = new Set(valueRef.current);
      if (newSet.has(userId)) {
        newSet.delete(userId);
      } else {
        newSet.add(userId);
      }
      onChange?.(newSet);
    },
    [onChange],
  );

  const handleSelectAll = useCallback(() => {
    const nextSelectedAll = !selectedAll;
    onSelectAll?.(nextSelectedAll);
    if (selectedAll) {
      onChange?.(new Set());
    } else {
      onChange?.(
        new Set(
          parsedData?.map(user => user[idKey as keyof typeof user] as string) ||
            [],
        ),
      );
    }
  }, [parsedData, selectedAll, idKey, onChange, onSelectAll]);

  const { virtualRows, rowVirtualizer } = useVirtualizer({
    scrollElementRef: scrollRef,
    registers: parsedData ?? [],
    virtualizerOptions: {
      estimateSize: () => ROW_HEIGHT,
      count: parsedData?.length ?? 0,
      getScrollElement: () => scrollRef.current,
      overscan: 5,
    },
    hasNextPage: usersQuery.hasNextPage,
    isFetchingNextPage: usersQuery.isFetchingNextPage,
    fetchNextPage: usersQuery.fetchNextPage,
  });

  const disableSearch = !parsedData?.length && !filterCount && !searchValue;
  const canSelectMore = selectionLimit ? value?.size < selectionLimit : true;
  const isLoadingUsers =
    usersQuery.isLoading ||
    (usersQuery.isFetching && !usersQuery.isFetchingNextPage);

  return (
    <Stack
      sx={{
        p: 2,
        height: '100%',
        backgroundColor: theme => theme.palette.new.background.elements.default,
        gap: 2,
        borderRadius: 2,
        ...sx,
      }}
    >
      {slotProps?.title && (
        <Title
          variant="S"
          {...slotProps?.title}
        />
      )}
      <Stack sx={{ flexDirection: 'row', gap: 2, alignItems: 'center' }}>
        {slotProps?.search && (
          <Search
            {...slotProps?.search}
            value={searchValue}
            onChange={onSearch}
            disabled={disableSearch}
          />
        )}
        {slotProps?.filterButton && (
          <Button
            startIcon={<IconFilter />}
            variant="secondary"
            sx={{ textWrap: 'nowrap' }}
            endIcon={
              (filterCount ?? 0) > 0 && (
                <Badge
                  sx={{ pl: 1.5 }}
                  badgeContent={filterCount}
                  color="primary"
                />
              )
            }
            {...slotProps?.filterButton}
          />
        )}
      </Stack>
      {slotProps?.filterPanel}
      <CardContainer
        padding={16}
        ref={scrollRef}
        sx={{
          backgroundColor: theme =>
            theme.palette.new.background.elements.default,
          borderRadius: 2,
          width: '100%',
          overflow: 'auto',
          flex: 1,
          minHeight: 0,
        }}
      >
        {isLoadingUsers && <SkeletonList />}
        {!parsedData?.length && !isLoadingUsers && (
          <StateCard
            title={slotProps?.emptyStateCard?.title ?? ''}
            description={slotProps?.emptyStateCard?.description ?? ''}
            slotProps={{
              ...slotProps?.emptyStateCard?.slotProps,
              card: {
                sx: {
                  border: 'none',
                  animation: `${appearFromBottom} 125ms ease-in-out backwards`,
                },
              },
            }}
          />
        )}
        {!!parsedData?.length && !isLoadingUsers && (
          <List
            sx={{
              p: 0,
              display: 'flex',
              flexDirection: 'column',
            }}
          >
            {slotProps?.selectAllCheckbox && (
              <SelectableListItem
                id="select-all"
                onSelect={handleSelectAll}
                selected={selectedAll}
                sx={{
                  py: 1,
                  mb: 2,
                  borderRadius: 1,
                }}
              >
                <Typography
                  variant="globalS"
                  color={theme => theme.palette.new.text.neutral.default}
                >
                  {slotProps?.selectAllCheckbox?.label}
                </Typography>
              </SelectableListItem>
            )}
            <Stack
              sx={{
                height: `${rowVirtualizer.getTotalSize()}px`,
                position: 'relative',
                width: '100%',
              }}
            >
              {virtualRows.map(virtualItem => {
                const user = parsedData![virtualItem.index];
                const userId = user[idKey as keyof typeof user] as string;
                const isSelected = value?.has(userId) || selectedAll;
                return (
                  <Stack
                    key={userId}
                    sx={{
                      position: 'absolute',
                      top: 0,
                      left: 0,
                      width: '100%',
                      transform: `translateY(${virtualItem.start}px)`,
                    }}
                  >
                    <SelectableListItem
                      id={userId}
                      disabled={!isSelected ? !canSelectMore : false}
                      selected={isSelected}
                      sx={ITEM_SX}
                      onSelect={handleUserSelect}
                    >
                      <UserAvatar
                        user={user}
                        {...slotProps?.userAvatar}
                      />
                    </SelectableListItem>
                  </Stack>
                );
              })}
            </Stack>
            {usersQuery.isFetchingNextPage && (
              <Stack
                sx={{
                  justifyContent: 'center',
                  alignItems: 'center',
                  py: 2,
                }}
              >
                <CircularProgress size={20} />
              </Stack>
            )}
          </List>
        )}
      </CardContainer>
    </Stack>
  );
};

export default IndividualSelection;
