import { useCallback, useMemo } from 'react';

import { useTranslation } from '../../../core/hooks/useTranslation';
import { useToast } from '../../../core/managers/ToastManager/hooks/useToast';
import { assertUnreachable } from '../../../utils/assertUnreachable';
import { ToastAppearance } from '../../Toast/constants';
import { TableSelectionType } from '../constants';
import { useTableCellContext } from '../contexts/TableCellContext';
import { useTablePropsContext } from '../contexts/TablePropsContext';
import { useTableSelectionContext } from '../contexts/TableSelectionContext';

/** Result of {@link useSingleRowSelection} hook. */
export interface UseSingleRowSelectionResult {
  /** Flag indicating current row is selected. */
  isSingleRowSelected: boolean;
  /** Function that will toggle current row selection. */
  onSingleRowSelectionChange: (selected: boolean) => void;
}

/** Hook that returns information about current row selection. */
export function useSingleRowSelection(): UseSingleRowSelectionResult {
  const { t } = useTranslation();
  const showToast = useToast();
  const { rowNumber } = useTableCellContext();
  const { selection, onSelectionChanged } = useTableSelectionContext();
  const { rows, rowIdProvider, rowSelectionIncludeLimit, rowSelectionName } = useTablePropsContext();
  const headerRowsCount = 1;

  const rowId = useMemo(() => {
    const dataRowNumber = rowNumber - headerRowsCount;
    return rowIdProvider(rows[dataRowNumber]);
  }, [rowIdProvider, rowNumber, rows]);

  const onSingleRowSelectionChange = useCallback(
    (selected: boolean) => {
      if (!onSelectionChanged) {
        return;
      }

      if (selection?.type === TableSelectionType.Exclude) {
        const unselectedIds = new Set(selection.unselectedIds);
        if (selected) {
          unselectedIds.delete(rowId);
        } else {
          unselectedIds.add(rowId);
        }

        onSelectionChanged({
          type: TableSelectionType.Exclude,
          unselectedIds: Array.from(unselectedIds),
        });
      } else {
        const selectedIds = new Set(selection?.selectedIds ?? []);
        if (selected) {
          selectedIds.add(rowId);
        } else {
          selectedIds.delete(rowId);
        }

        if (rowSelectionIncludeLimit && selectedIds.size > rowSelectionIncludeLimit) {
          const message = t('ui.table.selectionIncludeLimit', {
            count: rowSelectionIncludeLimit,
            actionText: t('ui.table.selectAll'),
            name: rowSelectionName,
          });
          showToast(message, { appearance: ToastAppearance.Warning });
        }

        onSelectionChanged({
          type: TableSelectionType.Include,
          selectedIds: Array.from(selectedIds).slice(0, rowSelectionIncludeLimit),
        });
      }
    },
    [onSelectionChanged, selection, rowId, rowSelectionIncludeLimit, t, rowSelectionName, showToast],
  );

  const isSingleRowSelected = useMemo(() => {
    if (!selection) {
      return false;
    }

    switch (selection.type) {
      case TableSelectionType.Exclude:
        return !selection.unselectedIds.has(rowId);
      case TableSelectionType.Include:
        return selection.selectedIds.has(rowId);
      /* istanbul ignore next */
      default:
        assertUnreachable(selection);
    }
  }, [rowId, selection]);

  return {
    onSingleRowSelectionChange,
    isSingleRowSelected,
  };
}
