"use client";

import { useCallback, useMemo, useState } from "react";

export interface UseTableSelectionReturn {
  /** Array of currently selected IDs */
  selectedIds: string[];
  /** Number of selected items (alias count) */
  selectedCount: number;
  /** Number of selected items */
  count: number;
  /** Check if a specific ID is selected */
  isSelected: (id: string) => boolean;
  /** Toggle selection state for a single ID */
  toggleSelect: (id: string) => void;
  /** Explicitly add a single ID to selection */
  selectOne: (id: string) => void;
  /** Explicitly remove a single ID from selection */
  deselectOne: (id: string) => void;
  /** Select all provided IDs (union or replace) */
  selectAll: (allIds: string[]) => void;
  /** Toggle select-all: deselects all if all are selected, otherwise selects all */
  toggleSelectAll: (allIds: string[]) => void;
  /** Clear all selections */
  clearSelection: () => void;
  /** Check if all provided IDs are selected */
  isAllSelected: (allIds: string[]) => boolean;
  /** Check if some (but not all) provided IDs are selected */
  isPartiallySelected: (allIds: string[]) => boolean;
  /** Select an inclusive range between two IDs in ordered list */
  selectRange: (fromId: string, toId: string, allOrderedIds: string[]) => void;
  /** Direct setter for selectedIds */
  setSelectedIds: React.Dispatch<React.SetStateAction<string[]>>;
}

export function useTableSelection(initialIds: string[] = []): UseTableSelectionReturn {
  const [selectedIds, setSelectedIds] = useState<string[]>(initialIds);

  const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);

  const isSelected = useCallback(
    (id: string): boolean => {
      return selectedSet.has(id);
    },
    [selectedSet]
  );

  const toggleSelect = useCallback((id: string) => {
    if (!id) return;
    setSelectedIds((prev) =>
      prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
    );
  }, []);

  const selectOne = useCallback((id: string) => {
    if (!id) return;
    setSelectedIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
  }, []);

  const deselectOne = useCallback((id: string) => {
    if (!id) return;
    setSelectedIds((prev) => prev.filter((item) => item !== id));
  }, []);

  const selectAll = useCallback((allIds: string[]) => {
    const validIds = Array.from(new Set(allIds.filter(Boolean)));
    setSelectedIds(validIds);
  }, []);

  const clearSelection = useCallback(() => {
    setSelectedIds([]);
  }, []);

  const isAllSelected = useCallback(
    (allIds: string[]): boolean => {
      if (!allIds || allIds.length === 0) return false;
      return allIds.every((id) => selectedSet.has(id));
    },
    [selectedSet]
  );

  const isPartiallySelected = useCallback(
    (allIds: string[]): boolean => {
      if (!allIds || allIds.length === 0) return false;
      const someSelected = allIds.some((id) => selectedSet.has(id));
      const allSelected = allIds.every((id) => selectedSet.has(id));
      return someSelected && !allSelected;
    },
    [selectedSet]
  );

  const toggleSelectAll = useCallback(
    (allIds: string[]) => {
      if (!allIds || allIds.length === 0) {
        setSelectedIds([]);
        return;
      }
      const allSelected = allIds.every((id) => selectedSet.has(id));
      if (allSelected) {
        // Deselect only the passed IDs (or clear if they represent all)
        setSelectedIds((prev) => prev.filter((id) => !allIds.includes(id)));
      } else {
        // Add all provided IDs
        setSelectedIds((prev) => Array.from(new Set([...prev, ...allIds])));
      }
    },
    [selectedSet]
  );

  const selectRange = useCallback(
    (fromId: string, toId: string, allOrderedIds: string[]) => {
      const fromIdx = allOrderedIds.indexOf(fromId);
      const toIdx = allOrderedIds.indexOf(toId);
      if (fromIdx === -1 || toIdx === -1) return;

      const start = Math.min(fromIdx, toIdx);
      const end = Math.max(fromIdx, toIdx);
      const rangeIds = allOrderedIds.slice(start, end + 1);

      setSelectedIds((prev) => Array.from(new Set([...prev, ...rangeIds])));
    },
    []
  );

  return {
    selectedIds,
    selectedCount: selectedIds.length,
    count: selectedIds.length,
    isSelected,
    toggleSelect,
    selectOne,
    deselectOne,
    selectAll,
    toggleSelectAll,
    clearSelection,
    isAllSelected,
    isPartiallySelected,
    selectRange,
    setSelectedIds,
  };
}
