import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
  useSyncExternalStore,
} from 'react';
import {
  matchPath,
  useLocation,
  useMatch,
  useNavigate,
} from 'react-router-dom';
import AssetManager, { getAssetFromVersion } from '../../lib/assets/AssetManager';
import useNotifyApiError from '../../hooks/useNotifyApiError';
import TagManager from '../../lib/assets/TagManager';
import { fetchAsset } from '../../lib/api/apiClient';
import { useEntryPointer as useEntryPointerCore } from '../features/selection/useEntryPointer';
import useDocumentsSelection from '../features/selection/useDocumentsSelection';
import useBulkDocumentActions from './useBulkDocumentActions';
import {
  DEFAULT_SORT_DIRECTION,
  DEFAULT_SORT_FIELD,
  mergeAssetIntoDocument,
} from '../../app/workspaceUtils';
import {
  createDocumentEntryKey,
  createFolderEntryKey,
  isFolderEntry,
  isDocumentEntry
} from '../../app/entryKey';
import useDocumentsSearch from '../../app/useDocumentsSearch';
import { useStatusToast } from '../../lib/context/StatusToastContext';
import useAuthManager from './useAuthManager';
import useTenantManager from './useTenantManager';
import useDocuments from './useDocuments';
import FoldersManager from '../FoldersManager';
import { fetchDocument } from '../../lib/api/apiClient';
import useFolderTree from '../features/folders/useFolderTree';
import useFolderTreeActions from '../features/folders/useFolderTreeActions';
import useDocumentTagActions from '../features/tagging/useDocumentTagActions';
import useDocumentUploads from '../features/upload/useDocumentUploads';
import useDocumentDragHandlers from '../features/upload/useDocumentDragHandlers';
import useDocumentMutations from './useDocumentMutations';
import useDetailWorkspace from '../../viewer/logic/useDetailWorkspace';
import useTags from './useTags';
import useCorrespondents from './useCorrespondents';
import useDocumentCorrespondentActions from '../features/correspondents/useDocumentCorrespondentActions';
import usePasskeys from '../../settings/usePasskeys';
import { resolveBreadcrumbs } from '../logic/breadcrumbs';
import useWorkspaceSelectionSync from '../features/selection/useWorkspaceSelectionSync';
import useWorkspaceViewData from './useWorkspaceViewData';
import useWorkspaceDragDrop from '../interactions/useWorkspaceDragDrop';
import { useManagementModals } from '../../app/useManagementModals';
import { useAppDispatch, useAppState } from '../../lib/store/appState';
import { listFolderContents } from '../../lib/api/apiClient';
import { useApi } from '../../lib/context/ApiContext';
import { useWorkspaceSelection } from '../../app/useWorkspaceSelection';
import useDocumentPreview from '../../app/useDocumentPreview';
import { createRootNode } from '../../app/workspaceUtils';
import type { DocumentId, FolderNodeId, Identifier } from '../../types/identifiers';

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

const noop = () => { };

import type { Document } from '../../types/documents';

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

interface UseDocumentsWorkspaceOptions {
  documentsViewMode?: string;
  documentsSortField?: string;
  documentsSortDirection?: string;
  onDocumentsViewModeChange?: (mode: string) => void;
  onDocumentsSortFieldChange?: (field: string) => void;
  onDocumentsSortDirectionToggle?: () => void;
  searchIncludeDescendants?: boolean;
  onSetSearchIncludeDescendants?: (value: boolean) => void;
}

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

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

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

  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 handleBreadcrumbNavigate = useCallback((crumb: { id?: Identifier | string } | null) => {
    if (!crumb || !crumb.id) {
      return;
    }
    const target = crumb.id === 'root' ? '/documents' : `/documents/folder/${crumb.id}`;
    navigate(target);
  }, [navigate]);

  const {
    status: appStatus,
    token,
    tenant,
    tenants: tenantOptionsRaw = [],
  } = appState;
  const { client: apiClient } = useApi();

  const tenantRecord = (tenant ?? null) as TenantOption | null;
  const currentTenantId: Identifier | null = (tenantRecord?.id ?? null) as Identifier | null;

  const tenantOptions: TenantOption[] = Array.isArray(tenantOptionsRaw)
    ? (tenantOptionsRaw as TenantOption[])
    : [];
  const { showToast } = useStatusToast();
  const notifyApiError = useNotifyApiError();
  const [creatingFolder, setCreatingFolder] = useState(false);
  const { handleLogout } = useAuthManager({});

  const tenantIdRef = useRef(currentTenantId);
  const detailPanelControlRef = useRef({ open: () => { }, close: () => { } });
  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<FolderNodeId | null>(null);
  const [activePreviewId, setActivePreviewId] = useState<DocumentId | null>(routeDocumentId || null);
  const shellRef = useRef(null);
  const assetManagerRef = useRef(null);
  if (!assetManagerRef.current) {
    const fetcher = async (id: Identifier) => {
      const asset = await fetchAsset(id);
      return (asset as unknown) as any;
    };
    assetManagerRef.current = new AssetManager({ fetchAsset: fetcher });
  }
  const assetManager = assetManagerRef.current;

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

  const fetchDocumentById = useCallback(
    async (documentId: DocumentId) => {
      if (!documentId) {
        return null;
      }
      const data = await fetchDocument(documentId);
      return extractDocumentFromResponse(data);
    },
    [extractDocumentFromResponse],
  );

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

  const selectionState = useWorkspaceSelection();

  const {
    selectedEntries,
    selectedDocumentIds,
    selectedFolderIds,
    setSelectedEntries,
    setSelectionOrder,
    selectionOrderRef,
    selectionAnchorRef,
    selectionInitializedRef,
    focusedDocumentId,
    setFocusedDocumentId,
    focusedEntryKey,
    setFocusedEntryKey,
    applySelection,
    handleEntrySelection,
    clearSelection,
    promoteSelectionOrder: promoteSelectionOrderRaw,
    configureSelectionEnvironment,
  } = selectionState;

  const {
    documents,
    setDocuments,
    removeDocumentsFromLookup,
    mapDocumentCaches,
    updateDocumentCaches,
    documentsManager,
  } = useDocuments({
    fetchDocumentById,
  });

  const documentLookup = useSyncExternalStore(
    (onStoreChange) => documentsManager.subscribe(onStoreChange),
    () => documentsManager.getSnapshot(),
    () => documentsManager.getSnapshot(),
  );

  const foldersManagerRef = useRef<FoldersManager | null>(null);
  if (!foldersManagerRef.current) {
    foldersManagerRef.current = new FoldersManager();
  }
  const foldersManager = foldersManagerRef.current;

  const folderStateRaw = useFolderTree({
    initialSelectedFolder: routeFolderId || 'root',
    foldersManager,
  });
  const {
    folderNodes,
    setFolderNodes,
    selectedFolder,
    setSelectedFolder,
    currentFolderName,
    folderOptions,
    isInvalidFolderDrop,
  } = folderStateRaw;

  const folderState = {
    ...folderStateRaw,
    setCreatingFolder,
  };

  const [currentSubfolders, setCurrentSubfolders] = useState<Array<{ id?: FolderNodeId; name?: string | null;[key: string]: unknown }>>([]);

  const reconcileSelectionWithFolderData = useCallback(
    (currentSelection: string[], docs: Document[], subfolders: any[]) => {
      const availableDocKeys = docs
        .map((doc) => createDocumentEntryKey(doc?.id as Identifier))
        .filter(Boolean);
      const availableDocKeySet = new Set(availableDocKeys);
      const availableFolderKeys = new Set(
        subfolders
          .map((folder) => createFolderEntryKey(folder?.id as Identifier))
          .filter(Boolean),
      );

      const previousFolderKeys = currentSelection
        .filter(isFolderEntry)
        .filter((key) => availableFolderKeys.has(key));
      const previousDocKeys = currentSelection.filter(isDocumentEntry);
      const nextDocKeys = previousDocKeys.filter((key) => availableDocKeySet.has(key));
      return [...previousFolderKeys, ...nextDocKeys];
    },
    [],
  );

  const selectedFolderRef = useRef<FolderNodeId>(selectedFolder);
  useEffect(() => {
    selectedFolderRef.current = selectedFolder;
  }, [selectedFolder]);

  const updateViewState = useCallback(
    (folderId: FolderNodeId, data: any, includeDocuments: boolean) => {
      // Guard against race conditions: only update if the folder is still selected
      if (folderId === selectedFolderRef.current) {
        if (includeDocuments) {
          setDocuments((data.documents || []) as Document[]);
        }
        setCurrentSubfolders((data.subfolders || []) as any[]);

        if (includeDocuments) {
          setSelectedEntries((prev) => reconcileSelectionWithFolderData(
            prev,
            (data.documents || []) as Document[],
            (data.subfolders || []) as any[]
          ));
        }
      }
    },
    [
      setDocuments,
      setSelectedEntries,
      setCurrentSubfolders,
      reconcileSelectionWithFolderData,
      selectedFolderRef,
    ]
  );

  const fetchFolderData = useCallback(
    async (
      folderId: FolderNodeId,
      options: { includeDocuments?: boolean } = {}
    ) => {
      const path = folderId === 'root' ? 'root' : folderId;
      const includeDocuments = options.includeDocuments ?? true;
      const params: Record<string, unknown> = {
        include_documents: includeDocuments,
        sort: activeSortFieldRef.current,
        dir: activeSortDirectionRef.current,
      };

      const data = await listFolderContents(path, params);
      return { data, includeDocuments };
    },
    [
      activeSortFieldRef,
      activeSortDirectionRef,
    ]
  );

  useEffect(() => {
    if (selectedFolder) {
      fetchFolderData(selectedFolder)
        .then(({ data, includeDocuments }) => {
          updateViewState(selectedFolder, data, includeDocuments);
        })
        .catch((error) => {
          notifyApiError(error, 'Failed to fetch folder contents');
        });
    }
  }, [selectedFolder, documentsSortField, documentsSortDirection, fetchFolderData, updateViewState, notifyApiError]);

  const {
    searchQuery,
    setSearchQuery,
    searchResultIds,
    setSearchResultIds,
    searchLoading,
    activeTagFilters,
    setActiveTagFilters,
    activeCorrespondentFilters,
    setActiveCorrespondentFilters,
    isFilterActive,
    documentsFilterValue,
  } = useDocumentsSearch({
    api: apiClient,
    selectedFolder,
    locationPathname: location.pathname,
    isDocumentsRoute,
    searchIncludeDescendants,
    documentsSortField,
    documentsSortDirection,
    setSearchIncludeDescendants,
    documentsManager,
  });

  const documentsFilter = documentsFilterValue;
  const showingSearchResults = searchResultIds !== null;

  const {
    viewDocuments,
    visibleEntryKeySet,
  } = useWorkspaceViewData({
    documents,
    documentLookup,
    searchResultIds,
    showingSearchResults,
    currentSubfolders,
  });

  const {
    openDocumentPreview,
    closeDocumentPreview,
    resetPreviewState,
  } = useDocumentPreview({
    routeDocumentId: previewDocumentId,
    documentsManager,
    selectedFolder,
    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());

  useWorkspaceSelectionSync({
    showingSearchResults,
    searchQuery,
    setSelectedEntries,
    setSelectionOrder,
    selectionOrderRef,
    selectionAnchorRef,
    setFocusedDocumentId,
    selectedDocumentIds,
    activePreviewId,
    setActivePreviewId,
    selectionInitializedRef,
  });

  const tagsStateRaw = useTags({
    tenantIdRef,
    tagManager,
    setActiveTagFilters,
    mapDocumentCaches,
  });
  const {
    tags,
    refreshTags,
    handleTagCreate,
    handleTagUpdate,
    handleTagDelete,
    setTags,
  } = tagsStateRaw;

  // tagLookupById is derived locally
  useEffect(() => {
    tenantIdRef.current = currentTenantId;
  }, [currentTenantId, tenantIdRef]);

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

  const tagsState = {
    ...tagsStateRaw,
    tagLookupById, // Add derived lookup
    tagManager,
  };

  const correspondentsStateRaw = useCorrespondents({
    tenantIdRef,
    mapDocumentCaches,
  });
  const {
    correspondents,
    refreshCorrespondents,
    handleCorrespondentCreate,
    handleCorrespondentUpdate,
    handleCorrespondentDelete,
    setCorrespondents,
  } = correspondentsStateRaw;

  const {
    correspondentLookupByName,
    handleDocumentCorrespondentAttach,
    handleCorrespondentRemove,
    handleCorrespondentAdd,
  } = useDocumentCorrespondentActions({
    correspondents,
    handleCorrespondentCreate,
    updateDocumentCaches,
  });

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

  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 refreshFolderData = useCallback(async () => {
    if (selectedFolder) {
      const { data, includeDocuments } = await fetchFolderData(selectedFolder);
      updateViewState(selectedFolder, data, includeDocuments);
    }
  }, [selectedFolder, fetchFolderData, updateViewState]);

  const handleManualRefresh = useCallback(async () => {
    try {
      await refreshFolderData();
      showToast('Folder refreshed successfully', 'success');
    } catch (error) {
      showToast('Failed to refresh folder', 'error');
      console.error('Failed to refresh folder:', error);
    }
  }, [refreshFolderData, showToast]);

  const {
    handleBulkTagAddFromDetail,
    handleBulkTagRemoveFromDetail,
    handleBulkSelectionReanalyze,
  } = useDocumentTagActions({
    tags,
    tagManager,
    refreshTags,
    resolveTargetDocumentIds,
    updateDocumentCaches,
  });

  const {
    dropOverlayState,
    handleFileDrop,
    uploadQueue,
    clearUploadQueue,
    resetUploadsState,
    handleFileSelection,
  } = useDocumentUploads({
    selectedFolder,
    currentFolderName,
    refreshCurrentFolder: refreshFolderData,
    shellRef,
  });

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

  const resetWorkspaceState = useCallback(() => {
    const rootNode = createRootNode();
    setFolderNodes(new Map([[rootNode.id, rootNode]]));
    setSelectedFolder('root');
    setCurrentSubfolders([]);
    setDocuments([]);
    setSelectedEntries([]);
    setSelectionOrder([]);
    selectionOrderRef.current = [];
    setFocusedDocumentId(null);
    selectionAnchorRef.current = null;
    setDraggedDocumentIds([]);
    setDraggedFolderId(null);
    setSearchResultIds(null);
    setTags([]);
    setCorrespondents([]);
    setSearchQuery('');
    setActiveTagFilters([]);
    setActiveCorrespondentFilters([]);
    setActivePreviewId(null);
    detailPanelControlRef.current.close();
    assetManager.reset();
    resetPreviewState();
    resetUploadsState();
    clearUploadQueue();

    detailFolderFetchRef.current = new Set();
    bootstrapInitializedRef.current = false;
    selectionInitializedRef.current = false;
    tenantIdRef.current = null;
  }, [
    assetManager,
    selectionAnchorRef,
    selectionInitializedRef,
    selectionOrderRef,
    setFocusedDocumentId,
    setSelectedEntries,
    setSelectionOrder,
    setFolderNodes,
    setSelectedFolder,
    setCurrentSubfolders,
    setDocuments,
    setDraggedDocumentIds,
    setDraggedFolderId,
    setSearchResultIds,
    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[]) => {
      if (!documentIds.length) {
        return;
      }

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

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

      removeDocumentsFromLookup(Array.from(idSet));
    },
    [
      setDocuments,
      setSearchResultIds,
      removeDocumentsFromLookup,
    ],
  );

  const documentsState = {
    documentLookup,
    setDocuments,
    setSearchResultIds,
    removeDocumentsFromCaches,
    updateDocumentCaches,
    mapDocumentCaches,
    extractDocumentFromResponse,
    ingestDocuments: (docs: unknown[]) => documentsManager.ingest(docs),
  };

  const actionsState = {
    closeDocumentPreview,
  };

  const {
    moveDocumentsToFolder,
    handleThumbnailRegeneration,
    handleDocumentsDelete,
    handleDocumentTagAdd,
    handleDocumentTagAttach,
    handleDocumentTitleUpdate,
    handleDocumentIssuedUpdate,
    handleDocumentTagDetach,
  } = useDocumentMutations({
    documentsState,
    folderState,
    selectionState,
    tagsState,
    actions: actionsState,
    previewDocumentId,
  });

  const dragState = {
    draggedDocumentIds,
    draggedFolderId,
    setDraggedDocumentIds,
    setDraggedFolderId,
  };

  const {
    loadFolder,
    selectFolder,
    handleFolderRename,
    handleFolderCreate,
    handleFolderDelete,
    folderClickHandlers,
  } = useFolderTreeActions({
    folderState,
    dragState,
    actions: {
      handleFileDrop,
      moveDocumentsToFolder,
    },
    utils: {
      isInvalidFolderDrop,
    },
  });

  const {
    clearDocumentSelection,
  } = useDocumentsSelection({
    showingSearchResults,
    currentSubfolders,
    visibleDocuments: viewDocuments,
    configureSelectionEnvironment,
    visibleEntryKeySet,
    selectedEntries,
    selectionAnchorRef,
    promoteSelectionOrderRaw,
    setFocusedDocumentId,
    setActivePreviewId,
    clearSelection,
    focusedDocumentId,
    setFocusedEntryKey,
    focusedEntryKey,
  });
  const initializeAfterLogin = useCallback(async () => {
    await Promise.all([
      refreshTags(),
      refreshCorrespondents(),
      foldersManager.ensureTree(),
    ]);
    const initialFolder = routeFolderId && routeFolderId !== 'root' ? routeFolderId : 'root';
    await loadFolder(initialFolder, {});
  }, [refreshTags, refreshCorrespondents, routeFolderId, loadFolder, foldersManager]);

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

    const targetParam = routeFolderId ?? 'root';

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

    // Checking cache (folderContents) is removed, now we rely on selectedFolder effect to fetch.
    if (targetParam !== selectedFolder) {
      selectFolder(targetParam, { immediate: true });
    }
  }, [
    token,
    appStatus,
    routeFolderId,
    routeDocumentId,
    selectedFolder,
    isFilterActive,
    selectFolder,
  ]);

  const mountedRef = useRef(true);
  useEffect(() => {
    return () => {
      mountedRef.current = false;
    };
  }, []);

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

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

    bootstrap();
  }, [appStatus, appDispatch, initializeAfterLogin]);

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

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

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

        if (!entry) {
          return null;
        }

        updateDocumentCaches(documentId, (doc) => mergeAssetIntoDocument(doc, entry));

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

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

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

  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]);

  useWorkspaceDragDrop({
    shellRef,
  });

  const {
    detailPanelProps,
    detailPanelOpen,
    openDetailPanel,
    previewActive,
    previewWorkspaceDocument,
    resolveFolderPath,
  } = useDetailWorkspace({
    documents: viewDocuments,
    documentLookup,
    folderNodes,
    detailPanelControlRef,
    detailFolderFetchRef,
    previewDocumentId,
    activePreviewId,
    openDocumentPreview: openDocumentPreviewForDetail,
    handleDocumentTitleUpdate,
    handleDocumentIssuedUpdate,
    handleDocumentTagAdd,
    handleDocumentTagDetach,
    ensureAssetUrl,
    getAsset: getDocumentAsset,
    correspondents,
    handleCorrespondentAdd,
    handleCorrespondentRemove,
    selectFolder,
    tags,
    tagLookupById,
  });

  const handleEntryPointerCore = useEntryPointerCore({
    onSelectEntry: (entry, event, { rowKey, modifierClick, primaryClick }) => {
      const { type, id } = entry;
      const key = rowKey
        || (type === EntryType.document ? createDocumentEntryKey(id) : createFolderEntryKey(id));
      if (key) {
        if (documentsViewMode === 'grid' && (event as any).shiftKey) {
          // Additive selection for Shift+Click in Grid View
          const newSelection = Array.from(new Set([...selectedEntries, key]));
          applySelection(newSelection, { anchor: key, interactedKeys: [key] });
        } else {
          handleEntrySelection(key, event);
        }
      }
      if (type === EntryType.folder && !modifierClick && primaryClick) {
        selectFolder(id);
      }
    },
  });

  const breadcrumbs = useMemo(() => {
    return resolveBreadcrumbs(selectedFolder || 'root', folderNodes as any);
  }, [selectedFolder, folderNodes]);

  const { handleTenantSelect } = useTenantManager({
    currentTenantId,
    handleDocumentsViewModeChange,
  });

  const sessionContext = {
    token,
    appStatus,
    handleLogout,
    tenant: tenantRecord,
    tenants: tenantOptions,
    tenantOptions,
    handleTenantSelect,
  };

  const uiContext = {
    notifyApiError,
    settingsOpen,
    openSettings,
    closeSettings,
    managementModals,
    refreshCurrentFolder: handleManualRefresh,
  };

  const uploadContext = {
    dropOverlayState,
    uploadQueue,
    clearUploadQueue,
    handleFileSelection,
  };

  const tagsContext = {
    tags,
    refreshTags,
    tagLookupById,
    activeTagFilters,
    handleTagUpdate,
    handleTagDelete,
    handleDocumentTagAttach,
    handleDocumentTagDetach,
    handleBulkTagAddFromDetail,
    handleBulkTagRemoveFromDetail,
    openTagsModal,
  };

  const correspondentsContext = {
    correspondents,
    refreshCorrespondents,
    activeCorrespondentFilters,
    handleCorrespondentUpdate,
    handleCorrespondentCreate,
    handleCorrespondentDelete,
    handleDocumentCorrespondentAttach,
    handleCorrespondentRemove,
    handleCorrespondentAdd,
    handleBulkCorrespondentAdd,
    handleBulkCorrespondentRemove,
    openCorrespondentsModal,
  };

  const passkeysContext = {
    passkeys,
    passkeysSupported,
    passkeysLoading,
    registeringPasskey,
    revokingPasskeyId,
    refreshPasskeys,
    registerPasskey,
    revokePasskey,
  };

  const previewContext = {
    previewActive,
    previewWorkspaceDocument,
    previewDocumentId,
    closeDocumentPreview,
    ensureAssetUrl,
    getDocumentAsset,
    handleThumbnailRegeneration,
  };

  const detailPanelContext = {
    detailPanelProps,
    detailPanelOpen,
    openDetailPanel,
  };

  const searchContext = {
    searchQuery,
    documentsFilter,
    searchLoading,
    documentsViewMode,
    handleDocumentsViewModeChange,
    documentsSortField,
    documentsSortDirection,
    handleDocumentsSortFieldChange,
    handleDocumentsSortDirectionToggle,
    searchResultIds,
    documents: viewDocuments,
  };

  const folderTreeContext = {
    foldersManager,
    selectedFolder,
    currentFolderName,
    folderOptions,
    handleBreadcrumbNavigate,
    resolveFolderPath,
    selectFolder,
    moveDocumentsToFolder,
    folderClickHandlers,
    handleFolderRename,
    handleFolderDelete,
    handleFolderDragStart,
    handleFolderDragEnd,
    draggedFolderId,
    handlePromptCreateFolder,
    creatingFolder,
    currentSubfolders,
    breadcrumbs,
  };

  const selectionContext = {
    clearDocumentSelection,
    handleDeleteSelection,
    handleEntryPointerCore,
    handleBulkSelectionReanalyze,
    selectionValue: selectionState,
  };

  const documentMutations = {
    handleDocumentTitleUpdate,
    handleDocumentDragStart,
    handleDocumentDragEnd,
    draggedDocumentIds,
  };

  const managers = {
    documentsManager,
    documentLookup,
  };

  const contextValue = {
    ...sessionContext,
    ...uiContext,
    ...uploadContext,
    ...tagsContext,
    ...correspondentsContext,
    ...passkeysContext,
    ...previewContext,
    ...detailPanelContext,
    ...searchContext,
    ...folderTreeContext,
    ...selectionContext,
    ...documentMutations,
    ...managers,
  };

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

export default useDocumentsWorkspace;
