import { MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  matchPath,
  useLocation,
  useMatch,
  useNavigate,
} from 'react-router-dom';
import AssetManager, { getAssetFromVersion } from '../../asset_manager';
import useApiError from '../useApiError';
import TagManager from '../../tag_manager';
import usePasskeys from '../../settings/usePasskeys';
import { useManagementModals } from '../../app/useManagementModals';
import { api, useAppDispatch, useAppState } from '../../app/appState';
import useWorkspaceSelection from '../../app/useWorkspaceSelection';
import { useEntryPointer as useEntryPointerCore } from '../../documents/useEntryPointer';
import { isTagTransferEvent } from '../../documents/tagTransfer';
import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection';
import useBulkDocumentActions from '../../documents/hooks/useBulkDocumentActions';
import useDocumentsPanelProps from '../../documents/hooks/useDocumentsPanelProps';
import useDocumentPreview from '../../app/useDocumentPreview';
import useDeskWorkspaceProps from '../../desktop/useDeskWorkspaceProps';
import useSidebarProps from '../../sidebar/useSidebarProps';
import {
  ASSET_PRESIGN_TTL_MS,
  DEFAULT_FOLDER_NAME,
  DEFAULT_SORT_DIRECTION,
  DEFAULT_SORT_FIELD,
  createRootNode,
  getRowId,
  isDocumentRowKey,
  isFolderRowKey,
  mergeAssetIntoDocument,
  resolveApiPath,
  resolveDocumentRowKey,
  resolveFolderRowKey,
} from '../../app/appLayoutUtils';
import useDocumentsSearch from '../../app/useDocumentsSearch';
import useDocumentsStore from './store/useDocumentsStore';
import useAuthManager from './useAuthManager';
import useTags from './useTags';
import useCorrespondents from './useCorrespondents';
import useTenantManager from './useTenantManager';
import useDocuments from './useDocuments';
import useFolderTree from './useFolderTree';
import useFolderTreeActions from './useFolderTreeActions';
import useDocumentTagging from './useDocumentTagging';
import useDocumentCorrespondentActions from './useDocumentCorrespondentActions';
import useDocumentUploads from './useDocumentUploads';
import useDocumentDragHandlers from './useDocumentDragHandlers';
import useDocumentMutations from './useDocumentMutations';
import useDetailWorkspace from '../../detail/useDetailWorkspace';

const EntryType = Object.freeze({
  document: 'document',
  folder: 'folder',
});

const noop = () => {};

type Identifier = string | number;
type DocumentId = Identifier;
type FolderId = Identifier | 'root';

interface DocumentLike {
  id?: DocumentId | null;
  title?: string | null;
  [key: string]: unknown;
}

interface FolderContentsEntry {
  folder?: { id?: FolderId; name?: string | null } | null;
  documents?: DocumentLike[];
  subfolders?: Array<{ id?: FolderId; name?: string | null; [key: string]: unknown }>;
  __includesDocuments?: boolean;
  __sortField?: string | null;
  __sortDirection?: string | null;
  [key: string]: unknown;
}

interface TenantOption {
  id?: Identifier | null;
  name?: string | null;
  slug?: string | null;
  [key: string]: unknown;
}

interface UseDocumentsWorkspaceOptions {
  documentsViewMode?: string;
  documentsSortField?: string;
  documentsSortDirection?: string;
  documentsSortFieldRef?: MutableRefObject<string>;
  documentsSortDirectionRef?: MutableRefObject<string>;
  onDocumentsViewModeChange?: (mode: string) => void;
  onDocumentsSortFieldChange?: (field: string) => void;
  onDocumentsSortDirectionToggle?: () => void;
  searchIncludeDescendants?: boolean;
  onToggleSearchIncludeDescendants?: () => void;
  onSetSearchIncludeDescendants?: (value: boolean) => void;
  sortRefreshReadyRef?: MutableRefObject<boolean>;
  handleDeskExit?: () => void;
}

const useDocumentsWorkspace = ({
  documentsViewMode = 'list',
  documentsSortField = DEFAULT_SORT_FIELD,
  documentsSortDirection = DEFAULT_SORT_DIRECTION,
  documentsSortFieldRef,
  documentsSortDirectionRef,
  onDocumentsViewModeChange,
  onDocumentsSortFieldChange,
  onDocumentsSortDirectionToggle,
  searchIncludeDescendants = true,
  onToggleSearchIncludeDescendants,
  onSetSearchIncludeDescendants,
  sortRefreshReadyRef,
  handleDeskExit,
}: UseDocumentsWorkspaceOptions = {}) => {
  const handleDocumentsViewModeChange = onDocumentsViewModeChange || noop;
  const handleDocumentsSortFieldChange = onDocumentsSortFieldChange || noop;
  const handleDocumentsSortDirectionToggle = onDocumentsSortDirectionToggle || noop;
  const toggleSearchIncludeDescendants = onToggleSearchIncludeDescendants || noop;
  const setSearchIncludeDescendants = onSetSearchIncludeDescendants || noop;
  const handleDeskExitSafe = handleDeskExit || noop;

  const fallbackSortFieldRef = useRef(documentsSortField);
  const activeSortFieldRef = documentsSortFieldRef || fallbackSortFieldRef;
  useEffect(() => {
    activeSortFieldRef.current = documentsSortField;
  }, [documentsSortField, activeSortFieldRef]);

  const fallbackSortDirectionRef = useRef(documentsSortDirection);
  const activeSortDirectionRef = documentsSortDirectionRef || fallbackSortDirectionRef;
  useEffect(() => {
    activeSortDirectionRef.current = documentsSortDirection;
  }, [documentsSortDirection, activeSortDirectionRef]);

  const fallbackSortRefreshReadyRef = useRef(false);
  const activeSortRefreshReadyRef = sortRefreshReadyRef || fallbackSortRefreshReadyRef;
  const navigate = useNavigate();
  const location = useLocation();
  const appState = useAppState();
  const appDispatch = useAppDispatch();
  const folderMatch = matchPath('/documents/folder/:folderId', location.pathname);
  const docMatch = matchPath('/documents/:documentId', location.pathname);
  const routeFolderId = folderMatch?.params?.folderId || null;
  const routeDocumentId = docMatch?.params?.documentId || null;
  const previewDocumentId = routeDocumentId;
  const {
    status: appStatus,
    token,
    tenant,
    tenants: tenantOptionsRaw = [],
  } = appState;

  const tenantRecord = (tenant ?? null) as TenantOption | null;
  const tenantName: string | null = typeof tenantRecord?.name === 'string'
    ? tenantRecord.name
    : typeof tenantRecord?.slug === 'string'
      ? tenantRecord.slug
      : null;

  const currentTenantId: Identifier | null = (() => {
    const value = tenantRecord?.id;
    if (typeof value === 'string' || typeof value === 'number') {
      return value as Identifier;
    }
    return null;
  })();

  const tenantOptions: TenantOption[] = Array.isArray(tenantOptionsRaw)
    ? (tenantOptionsRaw as TenantOption[])
    : [];
  const { status, setStatusMessage } = useDocumentsStore();
  const handleApiReport = useCallback(
    ({ message, variant }) => setStatusMessage(message, variant),
    [setStatusMessage],
  );
  const reportApiError = useApiError({
    onReport: handleApiReport,
  });
  const notifyApiError = useCallback(
    (error, fallbackMessage, variant = 'error') =>
      reportApiError(error, { message: fallbackMessage, variant }),
    [reportApiError],
  );
  const [loading, setLoading] = useState(false);
  const [creatingFolder, setCreatingFolder] = useState(false);
  const { tokenRef, handleLogout } = useAuthManager({
    apiClient: api,
    token,
    appStatus,
    appDispatch,
    notifyApiError,
    setStatusMessage,
    setLoading,
  });

  const breadcrumbFetchRef = useRef(new Set());
  const tagRemovalCursorActiveRef = useRef(false);
  const tenantIdRef = useRef(currentTenantId);
  const detailPanelControlRef = useRef({ open: () => {}, close: () => {} });
  const setTagRemovalCursor = useCallback((active) => {
    if (tagRemovalCursorActiveRef.current === active) {
      return;
    }
    const body = document.body;
    if (!body) {
      return;
    }
    tagRemovalCursorActiveRef.current = active;
    if (active) {
      body.classList.add('desk-cursor-remove');
    } else {
      body.classList.remove('desk-cursor-remove');
    }
  }, []);
  const documentsRouteMatch = useMatch('/documents');
  const documentsFolderRouteMatch = useMatch('/documents/folder/:folderId');
  const documentsDetailRouteMatch = useMatch('/documents/:documentId');
  const isDocumentsRoute = Boolean(
    documentsRouteMatch || documentsFolderRouteMatch || documentsDetailRouteMatch,
  );

  const [draggedDocumentIds, setDraggedDocumentIds] = useState<DocumentId[]>([]);
  const [draggedFolderId, setDraggedFolderId] = useState<FolderId | null>(null);
  const [activePreviewId, setActivePreviewId] = useState<DocumentId | null>(routeDocumentId || null);
  const shellRef = useRef(null);
  const assetManagerRef = useRef(null);
  if (!assetManagerRef.current) {
    assetManagerRef.current = new AssetManager({ api, assetPresignTtlMs: ASSET_PRESIGN_TTL_MS });
  }
  const assetManager = assetManagerRef.current;

  const extractDocumentFromResponse = useCallback(
    (payload) => {
      if (!payload) {
        return null;
      }
      const hydratedDetail = assetManager.hydrateDetail(payload);
      return hydratedDetail?.document || payload.document || payload;
    },
    [assetManager],
  );

  const tagManagerRef = useRef(null);
  if (!tagManagerRef.current) {
    tagManagerRef.current = new TagManager();
  }
  const tagManager = tagManagerRef.current;

  const selection = useWorkspaceSelection({
    resolveDocumentRowKey,
    resolveFolderRowKey,
    isDocumentRowKey,
    isFolderRowKey,
    getRowId,
  });

  const {
    selectedEntries,
    selectedDocumentIds,
    selectedFolderIds,
    setSelectedEntries,
    selectionOrder,
    setSelectionOrder,
    selectionOrderRef,
    selectionAnchorRef,
    selectionInitializedRef,
    focusedDocumentId,
    setFocusedDocumentId,
    focusedRowKey,
    setFocusedRowKey,
    applySelection,
    clearSelection,
    handleEntrySelection,
    promoteSelectionOrder: promoteSelectionOrderRaw,
    configureSelectionEnvironment,
  } = selection;

  const selectionHelpers = useMemo(
    () => ({
      setSelectedEntries,
      setSelectionOrder,
      selectionOrderRef,
      selectionAnchorRef,
      setFocusedDocumentId,
      focusedDocumentId,
      selectionInitializedRef,
    }),
    [
      setSelectedEntries,
      setSelectionOrder,
      selectionOrderRef,
      selectionAnchorRef,
      setFocusedDocumentId,
      focusedDocumentId,
      selectionInitializedRef,
    ],
  );

  const [folderContents, setFolderContents] = useState<Map<FolderId, FolderContentsEntry>>(
    () => new Map(),
  );
  const folderContentsRef = useRef(folderContents);
  useEffect(() => {
    folderContentsRef.current = folderContents;
  }, [folderContents]);

  const setSearchResultsRef = useRef(() => {});
  const setSearchResultsProxy = useCallback((value) => {
    if (typeof setSearchResultsRef.current === 'function') {
      setSearchResultsRef.current(value);
    }
  }, []);

  const {
    documents,
    setDocuments,
    mapDocumentCaches,
    updateDocumentCaches,
  } = useDocuments({
    setSearchResults: setSearchResultsProxy,
    setFolderContents,
  });

  const {
    folderNodes,
    setFolderNodes,
    selectedFolder,
    setSelectedFolder,
    currentFolder,
    setCurrentFolder,
    currentSubfolders,
    setCurrentSubfolders,
    currentFolderName,
    folderOptions,
    folderLabelMap,
    applySelectedFolder,
    ensureFolderData,
    ensureFolderAncestorsLoaded,
    expandFolderAncestors,
    isInvalidFolderDrop,
  } = useFolderTree({
    initialSelectedFolder: routeFolderId || 'root',
    assetManager,
    apiClient: api,
    tenantIdRef,
    documentsSortFieldRef: activeSortFieldRef,
    documentsSortDirectionRef: activeSortDirectionRef,
    selectionHelpers,
    setDocuments,
    setFolderContents,
    folderContentsRef,
  });

  const {
    searchQuery,
    setSearchQuery,
    searchResults,
    setSearchResults,
    searchLoading,
    activeTagFilters,
    setActiveTagFilters,
    activeCorrespondentFilters,
    setActiveCorrespondentFilters,
    toggleTagFilter,
    toggleCorrespondentFilter,
    isFilterActive,
    clearFilters,
    handleSearchChange,
    handleSearchSubmit,
  } = useDocumentsSearch({
    api,
    assetManager,
    token,
    selectedFolder,
    navigate,
    locationPathname: location.pathname,
    isDocumentsRoute,
    selectionHelpers,
    searchIncludeDescendants,
    documentsSortField,
    documentsSortDirection,
    notifyApiError,
    setLoading,
    setSearchIncludeDescendants,
  });

  useEffect(() => {
    setSearchResultsRef.current = setSearchResults;
  }, [setSearchResults]);

  const {
    previewEntries,
    previewDocuments,
    ensurePreviewData,
    openDocumentPreview,
    closeDocumentPreview,
    resetPreviewState,
    removePreviewEntries,
  } = useDocumentPreview({
    routeDocumentId: previewDocumentId,
    documents,
    searchResults,
    selectedFolder,
    assetManager,
    api,
    resolveApiPath,
    notifyApiError,
    navigate,
    locationPathname: location.pathname,
    locationSearch: location.search,
    detailPanelControlRef,
    setActivePreviewId,
  });

  const openDocumentPreviewForDetail = useCallback(
    ({ documentIds }: { documentIds?: Identifier[] } = {}) => {
      const targetId = documentIds?.find((value): value is Identifier => value != null);
      if (targetId == null) {
        return;
      }
      openDocumentPreview(targetId, { replace: true });
    },
    [openDocumentPreview],
  );

  const getDocumentAsset = useCallback((doc, type) => {
    if (!doc || !type) return null;
    return getAssetFromVersion(doc.current_version || null, type);
  }, []);

  const bootstrapInitializedRef = useRef(false);
  const detailFolderFetchRef = useRef(new Set());


  const showingSearchResults = searchResults !== null;

  const visibleDocuments = useMemo(
    () => (showingSearchResults ? searchResults : documents),
    [showingSearchResults, searchResults, documents],
  );

  const visibleDocumentIds = useMemo(
    () => visibleDocuments.map((doc) => doc.id),
    [visibleDocuments],
  );

  const visibleDocumentKeys = useMemo(
    () => visibleDocumentIds.map((id) => resolveDocumentRowKey(id)).filter(Boolean),
    [visibleDocumentIds],
  );

  const visibleFolderKeys = useMemo(
    () =>
      showingSearchResults
        ? []
        : currentSubfolders
            .map((folder) => resolveFolderRowKey(folder.id))
            .filter(Boolean),
    [showingSearchResults, currentSubfolders],
  );

  const visibleRowKeys = useMemo(
    () => [...visibleFolderKeys, ...visibleDocumentKeys],
    [visibleFolderKeys, visibleDocumentKeys],
  );

  const visibleRowKeySet = useMemo(
    () => new Set(visibleRowKeys),
    [visibleRowKeys],
  );

  const documentLookup = useMemo(() => {
    const map = new Map();
    const push = (items) => {
      (items || []).forEach((doc) => {
        if (doc?.id) {
          map.set(doc.id, doc);
        }
      });
    };

    push(documents);
    if (Array.isArray(searchResults)) {
      push(searchResults);
    }
    previewDocuments.forEach((doc, id) => {
      if (doc && id && !map.has(id)) {
        map.set(id, doc);
      }
    });
    return map;
  }, [documents, searchResults, previewDocuments]);

  const {
    tags,
    refreshTags,
    handleTagCreate,
    handleTagUpdate,
    handleTagDelete,
    setTags,
  } = useTags({
    apiClient: api,
    notifyApiError,
    setStatusMessage,
    tagManager,
    tenantIdRef,
    setActiveTagFilters,
    mapDocumentCaches,
  });


  useEffect(() => {
    tenantIdRef.current = currentTenantId;
  }, [currentTenantId]);


  useEffect(() => {
    if (!selectedDocumentIds.length) {
      return;
    }
    if (!selectedDocumentIds.includes(activePreviewId)) {
      setActivePreviewId(selectedDocumentIds[selectedDocumentIds.length - 1]);
    }
    selectionInitializedRef.current = true;
  }, [selectedDocumentIds, activePreviewId, selectionInitializedRef]);


  const tagLookupById = useMemo(() => {
    const map = new Map();
    tags.forEach((tag) => {
      if (tag?.id) {
        map.set(tag.id, tag);
      }
    });
    return map;
  }, [tags]);

  const {
    correspondents,
    refreshCorrespondents,
    handleCorrespondentCreate,
    handleCorrespondentUpdate,
    handleCorrespondentDelete,
    setCorrespondents,
  } = useCorrespondents({
    apiClient: api,
    notifyApiError,
    setStatusMessage,
    tenantIdRef,
    mapDocumentCaches,
  });

  const {
    passkeys,
    passkeysSupported,
    passkeysLoading,
    registeringPasskey,
    revokingPasskeyId,
    refreshPasskeys,
    registerPasskey,
    revokePasskey,
  } = usePasskeys({
    api,
    notifyApiError,
    setStatusMessage,
    token,
  });

  const resolveTargetDocumentIds = useCallback(
    (candidateIds) => {
      const normalized = Array.isArray(candidateIds)
        ? candidateIds.filter(Boolean)
        : [];
      if (normalized.length) {
        return Array.from(new Set(normalized));
      }
      return selectedDocumentIds;
    },
    [selectedDocumentIds],
  );

  const refreshCurrentFolder = useCallback(async () => {
    setLoading(true);
    try {
      const contents = await ensureFolderData(selectedFolder, {
        force: true,
        prefetchDepth: 1,
      });
      applySelectedFolder(selectedFolder, contents);
    } catch (error) {
      notifyApiError(error, 'Failed to refresh folder.');
    } finally {
      setLoading(false);
    }
  }, [selectedFolder, ensureFolderData, applySelectedFolder, notifyApiError]);

  const {
    handleBulkTagAddFromDetail,
    handleBulkTagRemoveFromDetail,
    handleBulkSelectionReanalyze,
  } = useDocumentTagging({
    apiClient: api,
    tags,
    tagManager,
    refreshTags,
    refreshCurrentFolder,
    resolveTargetDocumentIds,
    notifyApiError,
    setStatusMessage,
    setLoading,
  });

  const {
    dropOverlayState,
    handleFileDrop,
    handleFileSelection,
    uploadQueue,
    clearUploadQueue,
    resetUploadsState,
  } = useDocumentUploads({
    apiClient: api,
    token,
    selectedFolder,
    currentFolderName,
    ensureFolderData,
    refreshCurrentFolder,
    notifyApiError,
    setStatusMessage,
    setLoading,
    shellRef,
  });

  const {
    handleDocumentDragStart,
    handleDocumentDragEnd,
    handleFolderDragStart,
    handleFolderDragEnd,
  } = useDocumentDragHandlers({
    selectedEntries,
    selectedDocumentIds,
    selectedFolderIds,
    applySelection,
    handleEntrySelection,
    documentLookup,
    setDraggedDocumentIds,
    setDraggedFolderId,
    resolveDocumentRowKey,
    resolveFolderRowKey,
    documentsViewMode,
  });

  const {
    correspondentLookupByName,
    handleDocumentCorrespondentAttach,
    handleCorrespondentRemove,
    handleCorrespondentAdd,
  } = useDocumentCorrespondentActions({
    apiClient: api,
    correspondents,
    handleCorrespondentCreate,
    refreshCurrentFolder,
    notifyApiError,
    setStatusMessage,
  });

  useEffect(() => {
    if (!activeSortRefreshReadyRef.current) {
      activeSortRefreshReadyRef.current = true;
      return;
    }
    if (!isFilterActive && token) {
      refreshCurrentFolder();
    }
  }, [
    documentsSortField,
    documentsSortDirection,
    isFilterActive,
    refreshCurrentFolder,
    token,
    activeSortRefreshReadyRef,
  ]);

  const resetWorkspaceState = useCallback(() => {
    const rootNode = createRootNode();
    setFolderNodes(new Map([[rootNode.id, rootNode]]));
    setFolderContents(new Map());
    setSelectedFolder('root');
    setCurrentFolder(null);
    setCurrentSubfolders([]);
    setDocuments([]);
    setSelectedEntries([]);
    setSelectionOrder([]);
    selectionOrderRef.current = [];
    setFocusedDocumentId(null);
    selectionAnchorRef.current = null;
    setDraggedDocumentIds([]);
    setDraggedFolderId(null);
    setSearchResults(null);
    setTags([]);
    setCorrespondents([]);
    setSearchQuery('');
    setActiveTagFilters([]);
    setActiveCorrespondentFilters([]);
    setActivePreviewId(null);
    detailPanelControlRef.current.close();
    assetManager.reset();
    resetPreviewState();
    resetUploadsState();
    clearUploadQueue();
    breadcrumbFetchRef.current = new Set();
    detailFolderFetchRef.current = new Set();
    bootstrapInitializedRef.current = false;
    selectionInitializedRef.current = false;
    tenantIdRef.current = null;
  }, [
    assetManager,
    selectionAnchorRef,
    selectionInitializedRef,
    selectionOrderRef,
    setFocusedDocumentId,
    setSelectedEntries,
    setSelectionOrder,
    setFolderNodes,
    setFolderContents,
    setSelectedFolder,
    setCurrentFolder,
    setCurrentSubfolders,
    setDocuments,
    setDraggedDocumentIds,
    setDraggedFolderId,
    setSearchResults,
    setTags,
    setCorrespondents,
    setSearchQuery,
    setActiveTagFilters,
    setActiveCorrespondentFilters,
    setActivePreviewId,
    resetPreviewState,
    resetUploadsState,
    clearUploadQueue,
  ]);

  useEffect(() => {
    if (appStatus === 'logged-out' || appStatus === 'selecting-tenant') {
      resetWorkspaceState();
    }
  }, [appStatus, resetWorkspaceState]);


  const removeDocumentsFromCaches = useCallback(
    (documentIds: DocumentId[] | null | undefined) => {
      const safeIds = Array.isArray(documentIds) ? documentIds : [];
      if (!safeIds.length) {
        return;
      }

      const idSet = new Set<DocumentId>(safeIds);

      setDocuments((prev) => prev.filter((doc) => !idSet.has(doc.id)));
      setSearchResults((prev) => {
        if (!Array.isArray(prev)) {
          return prev;
        }
        const filtered = prev.filter((doc) => !idSet.has(doc.id));
        return filtered.length === prev.length ? prev : filtered;
      });

      setFolderContents((prev: Map<FolderId, FolderContentsEntry>) => {
        if (!prev.size) {
          return prev;
        }
        let changed = false;
        const next = new Map<FolderId, FolderContentsEntry>();
        prev.forEach((contents, key) => {
          const docs = Array.isArray(contents?.documents) ? contents.documents : null;
          if (!docs || docs.length === 0) {
            next.set(key, contents);
            return;
          }
          const filtered = docs.filter((doc) => !idSet.has(doc.id));
          if (filtered.length !== docs.length) {
            changed = true;
            next.set(key, { ...contents, documents: filtered });
          } else {
            next.set(key, contents);
          }
        });
        return changed ? next : prev;
      });

      removePreviewEntries(Array.from(idSet));
    },
    [setDocuments, setSearchResults, setFolderContents, removePreviewEntries],
  );

  const {
    moveDocumentsToFolder,
    handleThumbnailRegeneration,
    handleDocumentsDelete,
    handleDocumentTagAdd,
    handleDocumentTagAttach,
    handleDocumentTitleUpdate,
    handleDocumentIssuedUpdate,
    handleTagRemove,
  } = useDocumentMutations({
    api,
    token,
    documentLookup,
    folderLabelMap,
    ensureFolderData,
    selectedFolder,
    setSelectedFolder,
    setDocuments,
    setFolderContents,
    setSearchResults,
    setSelectedEntries,
    setSelectionOrder,
    selectionOrderRef,
    selectionAnchorRef,
    setFocusedDocumentId,
    focusedDocumentId,
    setFocusedRowKey,
    focusedRowKey,
    notifyApiError,
    setStatusMessage,
    setLoading,
    mapDocumentCaches,
    applySelectedFolder,
    folderNodes,
    setFolderNodes,
    removeDocumentsFromCaches,
    closeDocumentPreview,
    previewDocumentId,
    refreshCurrentFolder,
    documentsViewMode,
    updateDocumentCaches,
    tagLookupById,
    tags,
    refreshTags,
    tagManager,
    extractDocumentFromResponse,
  });

  const {
    loadFolder,
    selectFolder,
    handleFolderRename,
    handleFolderCreate,
    handleFolderDelete,
    folderClickHandlers,
  } = useFolderTreeActions({
    api,
    token,
    folderNodes,
    setFolderNodes,
    selectedFolder,
    setSelectedFolder,
    ensureFolderData,
    ensureFolderAncestorsLoaded,
    expandFolderAncestors,
    applySelectedFolder,
    notifyApiError,
    setStatusMessage,
    setLoading,
    setFolderContents,
    setCurrentFolder,
    setSearchResults,
    isFilterActive,
    navigate,
    handleFileDrop,
    moveDocumentsToFolder,
    draggedDocumentIds,
    draggedFolderId,
    setDraggedDocumentIds,
    setDraggedFolderId,
    isInvalidFolderDrop,
    setCreatingFolder,
  });

  const {
    promoteSelectionOrder,
    clearDocumentSelection,
  } = useDocumentsSelection({
    showingSearchResults,
    currentSubfolders,
    visibleDocuments,
    resolveFolderRowKey,
    resolveDocumentRowKey,
    configureSelectionEnvironment,
    visibleRowKeySet,
    selectedEntries,
    selectionAnchorRef,
    promoteSelectionOrderRaw,
    setFocusedDocumentId,
    setActivePreviewId,
    clearSelection,
    focusedDocumentId,
    setFocusedRowKey,
    focusedRowKey,
    isFolderRowKey,
  });
  const initializeAfterLogin = useCallback(async () => {
    setLoading(true);
    try {
      await Promise.all([refreshTags(), refreshCorrespondents()]);
      const initialFolder = routeFolderId && routeFolderId !== 'root' ? routeFolderId : 'root';
      await loadFolder(initialFolder, { showLoading: false });
    } catch (error) {
      notifyApiError(error, 'Failed to initialize data.');
      throw error;
    } finally {
      setLoading(false);
    }
  }, [refreshTags, refreshCorrespondents, routeFolderId, loadFolder, notifyApiError]);

  useEffect(() => {
    if (!token) {
      return;
    }
    if (appStatus !== 'ready' && appStatus !== 'bootstrapping') {
      return;
    }

    const targetParam = routeFolderId ?? 'root';

    if (targetParam === 'root' && routeDocumentId) {
      return;
    }

    const hasData = folderContents.has(targetParam);
    if (targetParam !== selectedFolder || !hasData) {
      selectFolder(targetParam, { immediate: true });
    }
  }, [
    token,
    appStatus,
    routeFolderId,
    routeDocumentId,
    selectedFolder,
    folderContents,
    isFilterActive,
    selectFolder,
  ]);



  useEffect(() => {
    if (appStatus !== 'authenticated') {
      return;
    }
    if (bootstrapInitializedRef.current) {
      return;
    }

    let cancelled = false;
    const bootstrap = async () => {
      bootstrapInitializedRef.current = true;
      appDispatch({ type: 'BOOTSTRAP_START' });
      try {
        await initializeAfterLogin();
        if (!cancelled) {
          appDispatch({ type: 'BOOTSTRAP_SUCCESS' });
        }
      } catch (error) {
        if (!cancelled) {
          appDispatch({
            type: 'BOOTSTRAP_FAILURE',
            error: error?.message || 'Failed to initialize data.',
          });
          bootstrapInitializedRef.current = false;
        }
      }
    };

    bootstrap();

    return () => {
      cancelled = true;
    };
  }, [appStatus, appDispatch, initializeAfterLogin]);

  const {
    handleBulkCorrespondentAdd,
    handleBulkCorrespondentRemove,
    handleDeleteSelection,
  } = useBulkDocumentActions({
    api,
    resolveTargetDocumentIds,
    correspondentLookupByName,
    handleCorrespondentCreate,
    refreshCurrentFolder,
    setStatusMessage,
    selectedDocumentIds,
    selectedFolderIds,
    handleDocumentsDelete,
    handleFolderDelete,
    clearDocumentSelection,
    setLoading,
  });



  const ensureAssetUrl = useCallback(
    async (documentId, asset, { force = false, start = null, limit = null } = {}) => {
      if (!documentId || !asset?.id) {
        return null;
      }

      try {
        const entry = await assetManager.ensureAsset(documentId, asset, {
          force,
          start,
          limit,
        });

        if (!entry) {
          return null;
        }

        setDocuments((prev) =>
          prev.map((doc) => (doc.id === documentId ? mergeAssetIntoDocument(doc, entry) : doc)),
        );

        setSearchResults((prev) =>
          Array.isArray(prev)
            ? prev.map((doc) => (doc.id === documentId ? mergeAssetIntoDocument(doc, entry) : doc))
            : prev,
        );

        return entry;
      } catch (error) {
        notifyApiError(error, 'Unable to refresh document asset.');
        throw error;
      }
    },
    [assetManager, setDocuments, setSearchResults, notifyApiError],
  );

  





  const handleDocumentTagDrop = useCallback(
    async (documentId, tag) => {
      if (!documentId || !tag?.id) {
        return;
      }

      if (tag.sourceDocId && tag.sourceDocId === documentId) {
        return;
      }

      const attached = await handleDocumentTagAttach({ documentId, tagId: tag.id, tag });
      if (!attached) {
        return;
      }

      if (tag.sourceDocId && tag.sourceDocId !== documentId) {
        await handleTagRemove(tag.sourceDocId, tag.id, {
          refreshTagList: false,
          showMessage: false,
        });
      }
    },
    [handleDocumentTagAttach, handleTagRemove],
  );



  const handlePromptCreateFolder = useCallback(async () => {
    if (creatingFolder) {
      return;
    }
    const input = window.prompt('New folder name');
    if (!input) {
      return;
    }
    const trimmed = input.trim();
    if (!trimmed) {
      setStatusMessage('Folder name cannot be empty.', 'error');
      return;
    }
    setCreatingFolder(true);
    try {
      const success = await handleFolderCreate(trimmed);
      if (!success) {
        setStatusMessage('Unable to create folder. Check the status message for details.', 'error');
      }
    } finally {
      setCreatingFolder(false);
    }
  }, [creatingFolder, handleFolderCreate, setStatusMessage]);

  const { managementModals, openTagsModal, openCorrespondentsModal } = useManagementModals({
    locationPathname: location.pathname,
    tags,
    refreshTags,
    onTagCreate: handleTagCreate,
    onTagUpdate: handleTagUpdate,
    onTagDelete: handleTagDelete,
    correspondents,
    refreshCorrespondents,
    onCorrespondentCreate: handleCorrespondentCreate,
    onCorrespondentUpdate: handleCorrespondentUpdate,
    onCorrespondentDelete: handleCorrespondentDelete,
    setStatusMessage,
  });

  const [settingsOpen, setSettingsOpen] = useState(false);
  const openSettings = useCallback(() => {
    setSettingsOpen(true);
  }, []);
  const closeSettings = useCallback(() => {
    setSettingsOpen(false);
  }, []);

  useEffect(() => {
    if (!settingsOpen) {
      return undefined;
    }
    const handleKeyDown = (event) => {
      if (event.key === 'Escape') {
        event.preventDefault();
        setSettingsOpen(false);
      }
    };
    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [settingsOpen]);

  useEffect(
    () => () => {
      setTagRemovalCursor(false);
    },
    [setTagRemovalCursor],
  );

  useEffect(() => {
    const host = shellRef.current;
    if (!host) {
      return undefined;
    }

    const isTagTransfer = (event) => isTagTransferEvent(event);

    const isDocumentDropTarget = (target) =>
      target instanceof Element ? Boolean(target.closest('[data-doc-id]')) : false;

    const handleTagDragOver = (event) => {
      if (!isTagTransfer(event)) {
        return;
      }
      if (isDocumentDropTarget(event.target)) {
        setTagRemovalCursor(false);
        return;
      }
      event.preventDefault();
      event.dataTransfer.dropEffect = 'move';
      setTagRemovalCursor(true);
    };

    const handleTagDragLeave = (event) => {
      if (!isTagTransfer(event)) {
        return;
      }
      const related = event.relatedTarget;
      if (related instanceof Element && host.contains(related)) {
        if (isDocumentDropTarget(related)) {
          setTagRemovalCursor(false);
        }
        return;
      }
      setTagRemovalCursor(false);
    };

    const handleTagDrop = async (event) => {
      if (!isTagTransfer(event)) {
        return;
      }
      setTagRemovalCursor(false);
      if (isDocumentDropTarget(event.target) || event.defaultPrevented) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      const raw =
        event.dataTransfer.getData('application/x-papercrate-tag') ||
        event.dataTransfer.getData('text/papercrate-tag');
      if (!raw) {
        return;
      }
      try {
        const payload = JSON.parse(raw);
        if (payload?.sourceDocId && payload?.id) {
          await handleTagRemove(payload.sourceDocId, payload.id, {
            refreshTagList: false,
            showMessage: true,
          });
        }
      } catch (error) {
        console.warn('Failed to remove tag from drop target', error);
      }
    };

    const handleTagDragEnd = () => {
      setTagRemovalCursor(false);
    };

    host.addEventListener('dragover', handleTagDragOver, true);
    host.addEventListener('dragleave', handleTagDragLeave, true);
    host.addEventListener('drop', handleTagDrop, true);
    window.addEventListener('dragend', handleTagDragEnd, true);

    return () => {
      host.removeEventListener('dragover', handleTagDragOver, true);
      host.removeEventListener('dragleave', handleTagDragLeave, true);
      host.removeEventListener('drop', handleTagDrop, true);
      window.removeEventListener('dragend', handleTagDragEnd, true);
      setTagRemovalCursor(false);
    };
  }, [handleTagRemove, setTagRemovalCursor]);


  const {
    detailPanelProps,
    detailPanelOpen,
    openDetailPanel,
    handleDetailPanelClose,
    inspectDocument,
    previewActive,
    previewWorkspaceDocument,
    previewWorkspaceEntry,
    resolveThumbnailUrlForDoc,
    resolveFolderPath,
  } = useDetailWorkspace({
    documents,
    searchResults,
    previewDocuments,
    selectionOrder,
    selectedDocumentIds,
    documentLookup,
    folderNodes,
    ensureFolderData,
    detailPanelControlRef,
    detailFolderFetchRef,
    previewEntries,
    previewDocumentId,
    activePreviewId,
    openDocumentPreview: openDocumentPreviewForDetail,
    handleDocumentTitleUpdate,
    handleDocumentIssuedUpdate,
    handleDocumentTagAdd,
    handleTagRemove,
    ensureAssetUrl,
    getDocumentAsset,
    ensurePreviewData,
    correspondents,
    handleCorrespondentAdd,
    handleCorrespondentRemove,
    resolveApiPath,
    selectFolder,
    tags,
    tagLookupById,
  });

  const inspectDocumentForDesk = useCallback(
    (docOrId: DocumentLike | Identifier | null | undefined) => {
      if (docOrId == null) {
        return;
      }
      const docId = typeof docOrId === 'object' ? docOrId?.id : docOrId;
      if (docId == null) {
        return;
      }
      inspectDocument(docId);
    },
    [inspectDocument],
  );

  const handleEntryPointerCore = useEntryPointerCore({
    resolveDocumentRowKey,
    resolveFolderRowKey,
    onSelectEntry: (entry, event, { rowKey, modifierClick, primaryClick }) => {
      const { type, id } = entry;
      const key = rowKey
        || (type === EntryType.document ? resolveDocumentRowKey(id) : resolveFolderRowKey(id));
      if (key) {
        handleEntrySelection(key, event);
      }
      if (type === EntryType.folder && !modifierClick && primaryClick) {
        selectFolder(id);
      }
    },
  });

  const { breadcrumbs, missingBreadcrumbAncestors } = useMemo(() => {
    const chain = [];
    const seen = new Set();
    const pending = new Set();
    let currentId = selectedFolder || 'root';
    let guard = 0;

    while (currentId && !seen.has(currentId) && guard < 32) {
      guard += 1;
      seen.add(currentId);

      if (currentId === 'root') {
        chain.push({ id: 'root', name: DEFAULT_FOLDER_NAME });
        currentId = null;
        break;
      }

      const node = folderNodes.get(currentId);
      if (node) {
        chain.push({ id: currentId, name: node.name || 'Folder' });
        currentId = node.parentId ?? 'root';
        continue;
      }

      let fallbackName = '…';
      let parentId = null;

      if (currentFolder && currentFolder.id === currentId) {
        fallbackName = currentFolder.name;
        parentId = currentFolder.parent_id ?? 'root';
      }

      chain.push({ id: currentId, name: fallbackName });
      pending.add(currentId);
      currentId = parentId;
    }

    if (!chain.some((crumb) => crumb.id === 'root')) {
      chain.push({ id: 'root', name: DEFAULT_FOLDER_NAME });
    }

    const ordered = [];
    const seenOrdered = new Set();
    chain
      .slice()
      .reverse()
      .forEach((crumb) => {
        if (!seenOrdered.has(crumb.id)) {
          seenOrdered.add(crumb.id);
          ordered.push(crumb);
        }
      });

    return { breadcrumbs: ordered, missingBreadcrumbAncestors: Array.from(pending) };
  }, [selectedFolder, folderNodes, currentFolder]);

  useEffect(() => {
    if (!missingBreadcrumbAncestors.length) {
      return;
    }

    missingBreadcrumbAncestors.forEach((folderId) => {
      if (!folderId || folderId === 'root') {
        return;
      }
      if (breadcrumbFetchRef.current.has(folderId)) {
        return;
      }

      breadcrumbFetchRef.current.add(folderId);
      ensureFolderData(folderId, { force: false })
        .catch((error) => {
          console.warn('Failed to preload breadcrumb ancestor', folderId, error);
        })
        .finally(() => {
          breadcrumbFetchRef.current.delete(folderId);
        });
    });
  }, [missingBreadcrumbAncestors, ensureFolderData]);

  const { handleTenantSelect } = useTenantManager({
    apiClient: api,
    appDispatch,
    currentTenantId,
    resetWorkspaceState,
    setStatusMessage,
    notifyApiError,
    setLoading,
    refreshTags,
    refreshCorrespondents,
    loadFolder,
    handleDocumentsViewModeChange,
    navigate,
    tokenRef,
    tenantIdRef,
  });


  const documentsTableProps = useDocumentsPanelProps({
    currentFolderName,
    breadcrumbs,
    refreshCurrentFolder,
    currentSubfolders,
    documents,
    searchResults,
    isFilterActive,
    folderClickHandlers,
    handleFolderDragStart,
    handleFolderDragEnd,
    draggedFolderId,
    handleFolderRename,
    openDocumentPreview,
    handleDocumentTitleUpdate,
    selectedDocumentIds,
    selectedFolderIds,
    focusedRowKey,
    draggedDocumentIds,
    handleDocumentDragStart,
    handleDocumentDragEnd,
    searchLoading,
    tagLookupById,
    activeCorrespondentFilters,
    selectedEntries,
    setFocusedRowKey,
    ensureAssetUrl,
    getDocumentAsset,
    toggleTagFilter,
    toggleCorrespondentFilter,
    handleDocumentTagDrop,
    documentsViewMode,
    documentsSortField,
    documentsSortDirection,
    handleDocumentsSortFieldChange,
    handleDocumentsSortDirectionToggle,
    searchIncludeDescendants,
    toggleSearchIncludeDescendants,
    handleDocumentsViewModeChange,
    clearDocumentSelection,
    handleDeleteSelection,
    handleEntryPointerCore,
    inspectDocument,
    handleEntrySelection,
    tags,
    correspondents,
    documentLookup,
    handleBulkTagAddFromDetail,
    handleBulkTagRemoveFromDetail,
    handleBulkCorrespondentAdd,
    handleBulkCorrespondentRemove,
    handleBulkSelectionReanalyze,
    folderOptions,
    moveDocumentsToFolder,
    selectFolder,
  });

  const sidebarProps = useSidebarProps({
    folderNodes,
    folderClickHandlers,
    handleFolderDelete,
    handleFolderRename,
    selectedFolder,
    handleFolderDragStart,
    handleFolderDragEnd,
    draggedFolderId,
    handlePromptCreateFolder,
    creatingFolder,
    tags,
    activeTagFilters,
    toggleTagFilter,
    handleTagCreate,
    correspondents,
    activeCorrespondentFilters,
    toggleCorrespondentFilter,
    handleCorrespondentCreate,
    appStatus,
    loading,
    previewActive,
    searchQuery,
    handleSearchChange,
    handleSearchSubmit,
    clearFilters,
    isFilterActive,
    handleLogout,
    status,
    tenantName,
    tenantOptions,
    currentTenantId,
    handleTenantSelect,
    openSettings,
    handleFileSelection,
    uploadQueue,
  });

  

  const deskWorkspaceProps = useDeskWorkspaceProps({
    documents,
    searchResults,
    breadcrumbs,
    currentFolderName,
    documentsViewMode,
    handleDocumentsViewModeChange,
    handleDeskExit: handleDeskExitSafe,
    refreshCurrentFolder,
    inspectDocument: inspectDocumentForDesk,
    handleEntryPointerCore,
    promoteSelectionOrder,
    currentTenantId,
    selectedDocumentIds,
    selectedFolderIds,
    clearDocumentSelection,
    detailPanelOpen,
    handleDetailPanelClose,
    resolveThumbnailUrlForDoc,
    handleDocumentTagDrop,
    handleTagRemove,
    ensureAssetUrl,
    getDocumentAsset,
    activeTagFilters,
    handleDeleteSelection,
    tags,
    correspondents,
    documentLookup,
    tagLookupById,
    handleBulkTagAddFromDetail,
    handleBulkTagRemoveFromDetail,
    handleBulkCorrespondentAdd,
    handleBulkCorrespondentRemove,
    handleBulkSelectionReanalyze,
    folderOptions,
    moveDocumentsToFolder,
    searchIncludeDescendants,
    toggleSearchIncludeDescendants,
    selectedEntries,
    selectionAnchorRef,
    applySelection,
    resolveDocumentRowKey,
    showingSearchResults,
    searchQuery,
    activeCorrespondentFilters,
    selectedFolder,
  });

  const contextValue = useMemo(
    () => ({
      token,
      appStatus,
      status,
      setStatusMessage,
      dropOverlayState,
      handleLogout,
      sidebarProps,
      tags,
      refreshTags,
      handleTagUpdate,
      handleTagDelete,
      handleDocumentTagDrop,
      correspondents,
      refreshCorrespondents,
      handleCorrespondentUpdate,
      handleCorrespondentCreate,
      handleCorrespondentDelete,
      handleDocumentCorrespondentAttach,
      handleCorrespondentRemove,
      handleCorrespondentAdd,
      passkeys,
      passkeysSupported,
      passkeysLoading,
      registeringPasskey,
      revokingPasskeyId,
      refreshPasskeys,
      registerPasskey,
      revokePasskey,
      previewActive,
      previewWorkspaceDocument,
      previewWorkspaceEntry,
      previewDocumentId,
      closeDocumentPreview,
      handleThumbnailRegeneration,
      documentsTableProps,
      detailPanelProps,
      documentsViewMode,
      deskWorkspaceProps,
      ensurePreviewData,
      ensureAssetUrl,
      resolveFolderPath,
      getDocumentAsset,
      notifyApiError,
      openTagsModal,
      openCorrespondentsModal,
      openSettings,
      detailPanelOpen,
      openDetailPanel,
      uploadQueue,
      clearUploadQueue,
    }),
    [
      token,
      appStatus,
      status,
      setStatusMessage,
      dropOverlayState,
      handleLogout,
      sidebarProps,
      tags,
      refreshTags,
      handleTagUpdate,
      handleTagDelete,
      handleDocumentTagDrop,
      correspondents,
      refreshCorrespondents,
      handleCorrespondentUpdate,
      handleCorrespondentCreate,
      handleCorrespondentDelete,
      handleDocumentCorrespondentAttach,
      handleCorrespondentRemove,
      handleCorrespondentAdd,
      passkeys,
      passkeysSupported,
      passkeysLoading,
      registeringPasskey,
      revokingPasskeyId,
      refreshPasskeys,
      registerPasskey,
      revokePasskey,
      previewActive,
      previewWorkspaceDocument,
      previewWorkspaceEntry,
      previewDocumentId,
      closeDocumentPreview,
      handleThumbnailRegeneration,
      documentsTableProps,
      detailPanelProps,
      documentsViewMode,
      deskWorkspaceProps,
      ensurePreviewData,
      ensureAssetUrl,
      resolveFolderPath,
      getDocumentAsset,
      notifyApiError,
      openTagsModal,
      openCorrespondentsModal,
      openSettings,
      detailPanelOpen,
      openDetailPanel,
      uploadQueue,
      clearUploadQueue,
    ],
  );

  // hook callers handle rendering / routing
  return {
    appStatus,
    location,
    shellRef,
    dropOverlayState,
    managementModals,
    contextValue,
    settingsOpen,
    closeSettings,
  };
};


export default useDocumentsWorkspace;
