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

interface FolderEntry {
  id: string | number;
  [key: string]: unknown;
}

interface DocumentEntry {
  id: string | number;
  [key: string]: unknown;
}

interface NavigableRow {
  key: string;
  type: 'folder' | 'document';
  id: string | number;
}

interface UseDocumentsSelectionOptions {
  showingSearchResults: boolean;
  currentSubfolders: FolderEntry[];
  visibleDocuments: DocumentEntry[];
  resolveFolderRowKey: (id: string | number) => string | null | undefined;
  resolveDocumentRowKey: (id: string | number) => string | null | undefined;
  configureSelectionEnvironment: (config: { visibleRowKeySet: Set<string>; navigableRowKeys: string[] }) => void;
  visibleRowKeySet: Set<string>;
  selectedEntries: string[];
  selectionAnchorRef: { current: string | null };
  promoteSelectionOrderRaw: (id: string | number) => void;
  setFocusedDocumentId: (id: string | number | null) => void;
  setActivePreviewId: (id: string | number | null) => void;
  clearSelection: () => void;
  focusedDocumentId: string | number | null;
  setFocusedRowKey: (value: string | null | ((current: string | null) => string | null)) => void;
  focusedRowKey: string | null;
  isFolderRowKey: (key: string | null) => boolean;
}

const useDocumentsSelection = ({
  showingSearchResults,
  currentSubfolders,
  visibleDocuments,
  resolveFolderRowKey,
  resolveDocumentRowKey,
  configureSelectionEnvironment,
  visibleRowKeySet,
  selectedEntries,
  selectionAnchorRef,
  promoteSelectionOrderRaw,
  setFocusedDocumentId,
  setActivePreviewId,
  clearSelection,
  focusedDocumentId,
  setFocusedRowKey,
  focusedRowKey,
  isFolderRowKey,
}: UseDocumentsSelectionOptions) => {
  const navigableRows = useMemo<NavigableRow[]>(() => {
    const entries: NavigableRow[] = [];
    if (!showingSearchResults) {
      currentSubfolders.forEach((folder) => {
        const key = resolveFolderRowKey(folder.id);
        if (key) {
          entries.push({ key, type: 'folder', id: folder.id });
        }
      });
    }
    visibleDocuments.forEach((doc) => {
      const key = resolveDocumentRowKey(doc.id);
      if (key) {
        entries.push({ key, type: 'document', id: doc.id });
      }
    });
    return entries;
  }, [showingSearchResults, currentSubfolders, visibleDocuments, resolveFolderRowKey, resolveDocumentRowKey]);

  const navigableRowKeys = useMemo(
    () => navigableRows.map((entry) => entry.key),
    [navigableRows],
  );

  useEffect(() => {
    configureSelectionEnvironment({
      visibleRowKeySet,
      navigableRowKeys,
    });
  }, [configureSelectionEnvironment, visibleRowKeySet, navigableRowKeys]);

  const promoteSelectionOrder = useCallback(
    (docId: string | number | null) => {
      if (!docId) return;
      promoteSelectionOrderRaw(docId);
      const rowKey = resolveDocumentRowKey(docId);
      if (rowKey) {
        selectionAnchorRef.current = rowKey;
      }
      setFocusedDocumentId(docId);
      setActivePreviewId(docId);
    },
    [promoteSelectionOrderRaw, resolveDocumentRowKey, selectionAnchorRef, setFocusedDocumentId, setActivePreviewId],
  );

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

  const prevFocusedDocIdRef = useRef<string | number | null>(focusedDocumentId);
  useEffect(() => {
    const previous = prevFocusedDocIdRef.current;
    if (previous === focusedDocumentId) {
      return;
    }
    prevFocusedDocIdRef.current = focusedDocumentId;
    if (focusedDocumentId) {
      setFocusedRowKey(resolveDocumentRowKey(focusedDocumentId));
    } else {
      setFocusedRowKey((current) => (isFolderRowKey(current) ? current : null));
    }
  }, [focusedDocumentId, resolveDocumentRowKey, setFocusedRowKey, isFolderRowKey]);

  useEffect(() => {
    if (!navigableRowKeys.length) {
      if (focusedRowKey) {
        setFocusedRowKey(null);
      }
      return;
    }

    if (focusedRowKey && navigableRowKeys.includes(focusedRowKey)) {
      return;
    }

    const docKey = focusedDocumentId ? resolveDocumentRowKey(focusedDocumentId) : null;
    if (docKey && navigableRowKeys.includes(docKey)) {
      setFocusedRowKey(docKey);
      return;
    }

    const selectedKey = selectedEntries.find((key) => navigableRowKeys.includes(key));
    if (selectedKey) {
      setFocusedRowKey(selectedKey);
      return;
    }

    if (focusedRowKey) {
      setFocusedRowKey(null);
    }
  }, [focusedRowKey, focusedDocumentId, navigableRowKeys, selectedEntries, setFocusedRowKey, resolveDocumentRowKey]);

  return {
    navigableRows,
    navigableRowKeys,
    promoteSelectionOrder,
    clearDocumentSelection,
  };
};

export default useDocumentsSelection;
