import { useCallback, useState } from 'react';

type HeaderState = {
  checked: boolean;
  indeterminate: boolean;
};

const toggleInSet = (set: Set<string>, id: string) => {
  const next = new Set(set);
  if (next.has(id)) {
    next.delete(id);
  } else {
    next.add(id);
  }
  return next;
};

export const useApprovalSelection = () => {
  const [selectedIds, setSelectedIds] = useState<Set<string>>(() => new Set());
  const [allRows, setAllRows] = useState(false);
  const [excludedIds, setExcludedIds] = useState<Set<string>>(() => new Set());

  const isRowSelected = useCallback(
    (id: string) => (allRows ? !excludedIds.has(id) : selectedIds.has(id)),
    [allRows, excludedIds, selectedIds],
  );

  const getHeaderState = useCallback(
    (visibleIds: string[]): HeaderState => {
      if (visibleIds.length === 0) {
        return { checked: false, indeterminate: false };
      }
      const selectedVisibleCount = visibleIds.reduce(
        (acc, id) => acc + (isRowSelected(id) ? 1 : 0),
        0,
      );
      if (selectedVisibleCount === 0) {
        return { checked: false, indeterminate: false };
      }
      if (selectedVisibleCount === visibleIds.length) {
        return { checked: true, indeterminate: false };
      }
      return { checked: false, indeterminate: true };
    },
    [isRowSelected],
  );

  const toggleRow = useCallback(
    (id: string) => {
      if (allRows) {
        setExcludedIds(prev => toggleInSet(prev, id));
        return;
      }
      setSelectedIds(prev => toggleInSet(prev, id));
    },
    [allRows],
  );

  const toggleHeader = useCallback(
    (visibleIds: string[]) => {
      if (visibleIds.length === 0) return;

      const toggleSet = (prev: Set<string>, shouldRemove: boolean) => {
        const next = new Set(prev);
        for (const id of visibleIds) {
          if (shouldRemove) next.delete(id);
          else next.add(id);
        }
        return next;
      };

      if (allRows) {
        setExcludedIds(prev => {
          const allVisibleIncluded = visibleIds.every(id => !prev.has(id));
          return toggleSet(prev, !allVisibleIncluded);
        });
      } else {
        setSelectedIds(prev => {
          const allVisibleSelected = visibleIds.every(id => prev.has(id));
          return toggleSet(prev, allVisibleSelected);
        });
      }
    },
    [allRows],
  );

  const selectAllAcrossFilters = useCallback(() => {
    setAllRows(true);
    setSelectedIds(new Set());
    setExcludedIds(new Set());
  }, []);

  const reset = useCallback(() => {
    setAllRows(false);
    setSelectedIds(new Set());
    setExcludedIds(new Set());
  }, []);

  return {
    selectedIds,
    allRows,
    excludedIds,
    isRowSelected,
    getHeaderState,
    toggleRow,
    toggleHeader,
    selectAllAcrossFilters,
    deselectAll: reset,
    reset,
  };
};
