import {
  MutableRefObject,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
  useSyncExternalStore,
} 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 { useManagementModals } from '../../app/useManagementModals';
import { useAppDispatch, useAppState } from '../../app/appState';
import { fetchAsset } from '../../lib/apiClient';
import { useApi } from '../../app/ApiContext';
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 useSidebarProps from '../../sidebar/useSidebarProps';
import {
  DEFAULT_SORT_DIRECTION,
  DEFAULT_SORT_FIELD,
  createRootNode,
  mergeAssetIntoDocument,
} from '../../app/workspaceUtils';
import { createDocumentEntryKey, createFolderEntryKey } from '../../app/entryKey';
import useDocumentsSearch from '../../app/useDocumentsSearch';
import useDocumentsStore from './store/useDocumentsStore';
import useAuthManager from './useAuthManager';
import useTenantManager from './useTenantManager';
import useDocuments from './useDocuments';
import { fetchDocument } from '../../lib/apiClient';
import useFolderTree from './useFolderTree';
import useFolderTreeActions from './useFolderTreeActions';
import useDocumentTagging from './useDocumentTagging';
import useDocumentUploads from './useDocumentUploads';
import useDocumentDragHandlers from './useDocumentDragHandlers';
import useDocumentMutations from './useDocumentMutations';
import useDetailWorkspace from '../../detail/useDetailWorkspace';
import useWorkspaceTaxonomies from './useWorkspaceTaxonomies';
import useWorkspaceBreadcrumbs from './useWorkspaceBreadcrumbs';
import useWorkspaceSelectionSync from './useWorkspaceSelectionSync';
import type { DocumentId, FolderId as FolderIdentifier, Identifier } from '../../types/identifiers';

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

const noop = () => { };

type FolderId = FolderIdentifier | 'root';

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

interface FolderContentsEntry {
  folder?: { id?: FolderId; name?: string | null } | null;
  documents?: Document[];
  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;
  onSetSearchIncludeDescendants?: (value: boolean) => void;
  sortRefreshReadyRef?: MutableRefObject<boolean>;
}

const useDocumentsWorkspace = ({
  documentsViewMode = 'list',
  documentsSortField = DEFAULT_SORT_FIELD,
  documentsSortDirection = DEFAULT_SORT_DIRECTION,
  documentsSortFieldRef,
  documentsSortDirectionRef,
  onDocumentsViewModeChange,
  onDocumentsSortFieldChange,
  onDocumentsSortDirectionToggle,
  searchIncludeDescendants = true,
  onSetSearchIncludeDescendants,
  sortRefreshReadyRef,
}: UseDocumentsWorkspaceOptions = {}) => {
  const handleDocumentsViewModeChange = onDocumentsViewModeChange || noop;
  const handleDocumentsSortFieldChange = onDocumentsSortFieldChange || noop;
  const handleDocumentsSortDirectionToggle = onDocumentsSortDirectionToggle || noop;
  const setSearchIncludeDescendants = onSetSearchIncludeDescendants || 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 { client: apiClient } = useApi();

  const tenantRecord = (tenant ?? null) as TenantOption | null;
  const tenantNameCandidate = tenantRecord?.name ?? tenantRecord?.slug ?? null;
  const tenantName = tenantNameCandidate ? String(tenantNameCandidate) : null;

  const currentTenantId: Identifier | null = (tenantRecord?.id ?? null) as Identifier | 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 [creatingFolder, setCreatingFolder] = useState(false);
  const { tokenRef, handleLogout } = useAuthManager({
    token,
    appStatus,
    appDispatch,
    setStatusMessage,
  });

  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) {
    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 selection = useWorkspaceSelection();

  const {
    selectedEntries,
    selectedDocumentIds,
    selectedFolderIds,
    setSelectedEntries,
    selectionOrder,
    setSelectionOrder,
    selectionOrderRef,
    selectionAnchorRef,
    selectionInitializedRef,
    focusedDocumentId,
    setFocusedDocumentId,
    focusedEntryKey,
    setFocusedEntryKey,
    applySelection,
    handleEntrySelection,
    clearSelection,
    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 {
    documents,
    setDocuments,
    removeDocumentsFromLookup,
    mapDocumentCaches,
    updateDocumentCaches,
    documentsManager,
  } = useDocuments({
    setFolderContents,
    fetchDocumentById,
  });

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

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

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

  const documentsFilter = documentsFilterValue;

  const [visibleDocumentIds, setVisibleDocumentIds] = useState<DocumentId[]>([]);

  const showingSearchResults = searchResultIds !== null;

  useEffect(() => {
    const arraysEqual = (a: DocumentId[], b: DocumentId[]) =>
      a.length === b.length && a.every((value, index) => value === b[index]);

    if (showingSearchResults && Array.isArray(searchResultIds)) {
      const ids = searchResultIds.filter((id): id is DocumentId => id != null);
      setVisibleDocumentIds((prev) => (arraysEqual(prev, ids) ? prev : ids));
      return;
    }

    const folderIds = documents
      .map((doc) => (doc?.id ?? null) as DocumentId | null)
      .filter((id): id is DocumentId => id != null);
    setVisibleDocumentIds((prev) => (arraysEqual(prev, folderIds) ? prev : folderIds));
  }, [showingSearchResults, searchResultIds, documents]);

  const viewDocuments = useMemo(
    () =>
      visibleDocumentIds
        .map((id) => documentLookup.get(id) || null)
        .filter((doc): doc is Document => Boolean(doc)),
    [visibleDocumentIds, documentLookup],
  );

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

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

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

  const visibleEntryKeySet = useMemo(
    () => new Set(visibleEntryKeys),
    [visibleEntryKeys],
  );

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

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

  const {
    tags,
    refreshTags,
    handleTagCreate,
    handleTagUpdate,
    handleTagDelete,
    setTags,
    tagLookupById,
    correspondents,
    refreshCorrespondents,
    handleCorrespondentCreate,
    handleCorrespondentUpdate,
    handleCorrespondentDelete,
    setCorrespondents,
    correspondentLookupByName,
    handleDocumentCorrespondentAttach,
    handleCorrespondentRemove,
    handleCorrespondentAdd,
    passkeys,
    passkeysSupported,
    passkeysLoading,
    registeringPasskey,
    revokingPasskeyId,
    refreshPasskeys,
    registerPasskey,
    revokePasskey,
  } = useWorkspaceTaxonomies({
    apiClient,
    notifyApiError,
    setStatusMessage,
    tagManager,
    tenantIdRef,
    currentTenantId,
    setActiveTagFilters,
    mapDocumentCaches,
    updateDocumentCaches,
    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 () => {
    const contents = await ensureFolderData(selectedFolder, {
      force: true,
      prefetchDepth: 1,
    });
    applySelectedFolder(selectedFolder, contents);
  }, [selectedFolder, ensureFolderData, applySelectedFolder]);

  const {
    handleBulkTagAddFromDetail,
    handleBulkTagRemoveFromDetail,
    handleBulkSelectionReanalyze,
  } = useDocumentTagging({
    apiClient,
    tags,
    tagManager,
    refreshTags,
    resolveTargetDocumentIds,
    notifyApiError,
    setStatusMessage,
    updateDocumentCaches,
  });

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

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

  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);
    setSearchResultIds(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,
    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;
      });

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

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

  const {
    moveDocumentsToFolder,
    handleThumbnailRegeneration,
    handleDocumentsDelete,
    handleDocumentTagAdd,
    handleDocumentTagAttach,
    handleDocumentTitleUpdate,
    handleDocumentIssuedUpdate,
    handleTagRemove,
  } = useDocumentMutations({
    token,
    documentLookup,
    folderLabelMap,
    ensureFolderData,
    selectedFolder,
    setSelectedFolder,
    setDocuments,
    setFolderContents,
    setSearchResultIds,
    setSelectedEntries,
    setSelectionOrder,
    selectionOrderRef,
    selectionAnchorRef,
    setFocusedDocumentId,
    focusedDocumentId,
    setFocusedEntryKey,
    focusedEntryKey,
    notifyApiError,
    setStatusMessage,
    mapDocumentCaches,
    applySelectedFolder,
    folderNodes,
    setFolderNodes,
    removeDocumentsFromCaches,
    closeDocumentPreview,
    previewDocumentId,
    refreshCurrentFolder,
    updateDocumentCaches,
    tagLookupById,
    tags,
    refreshTags,
    tagManager,
    extractDocumentFromResponse,
    ingestDocuments: (docs) => documentsManager.ingest(docs),
  });

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

  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()]);
    const initialFolder = routeFolderId && routeFolderId !== 'root' ? routeFolderId : 'root';
    await loadFolder(initialFolder, {});
  }, [refreshTags, refreshCorrespondents, routeFolderId, loadFolder]);

  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({
    resolveTargetDocumentIds,
    correspondentLookupByName,
    handleCorrespondentCreate,
    setStatusMessage,
    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 handleDocumentTagDrop = useCallback(
    async (documentId, tagId) => {
      if (!documentId || !tagId) {
        return;
      }

      await handleDocumentTagAttach({ documentId, tagId });
    },
    [handleDocumentTagAttach],
  );

  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) {
      setStatusMessage('Folder name cannot be empty.', 'error');
      return;
    }
    setCreatingFolder(true);
    try {
      const success = await handleFolderCreate(trimmed, parentId);
      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,
    inspectDocument,
    previewActive,
    previewWorkspaceDocument,
    resolveFolderPath,
  } = useDetailWorkspace({
    documents: viewDocuments,
    selectionOrder,
    selectedDocumentIds,
    documentLookup,
    folderNodes,
    ensureFolderData,
    detailPanelControlRef,
    detailFolderFetchRef,
    previewDocumentId,
    activePreviewId,
    openDocumentPreview: openDocumentPreviewForDetail,
    handleDocumentTitleUpdate,
    handleDocumentIssuedUpdate,
    handleDocumentTagAdd,
    handleTagRemove,
    ensureAssetUrl,
    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 = useWorkspaceBreadcrumbs({
    selectedFolder,
    folderNodes,
    currentFolder,
    breadcrumbFetchRef,
    ensureFolderData,
  });

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

  const documentsPanelProps = useDocumentsPanelProps({
    currentFolderName,
    breadcrumbs,
    refreshCurrentFolder,
    currentSubfolders,
    documents: viewDocuments,
    searchQuery,
    searchResultIds,
    folderClickHandlers,
    selectedFolder,
    handleFolderDragStart,
    handleFolderDragEnd,
    draggedFolderId,
    handleFolderRename,
    openDocumentPreview,
    handleDocumentTitleUpdate,
    draggedDocumentIds,
    handleDocumentDragStart,
    handleDocumentDragEnd,
    searchLoading,
    tagLookupById,
    activeTagFilters,
    activeCorrespondentFilters,
    ensureAssetUrl,
    getDocumentAsset,
    handleDocumentTagAttach: handleDocumentTagDrop,
    handleDocumentTagDetach: handleTagRemove,
    documentsViewMode,
    documentsSortField,
    documentsSortDirection,
    handleDocumentsSortFieldChange,
    handleDocumentsSortDirectionToggle,
    handleDocumentsViewModeChange,
    clearDocumentSelection,
    handleDeleteSelection,
    handleEntryPointerCore,
    onDocumentActivate: inspectDocument,
    tags,
    correspondents,
    documentLookup,
    handleBulkTagAddFromDetail,
    handleBulkTagRemoveFromDetail,
    handleBulkCorrespondentAdd,
    handleBulkCorrespondentRemove,
    handleBulkSelectionReanalyze,
    folderOptions,
    moveDocumentsToFolder,
    selectFolder,
    documentLinks,
    ensureDownloadUrl,
    selectionValue: selection,
    currentTenantId,
  });
  const documentsTableProps = useMemo(
    () => ({
      ...documentsPanelProps,
    }),
    [documentsPanelProps],
  );

  const sidebarProps = useSidebarProps({
    folderNodes,
    folderClickHandlers,
    handleFolderDelete,
    handleFolderRename,
    selectedFolder,
    handleFolderDragStart,
    handleFolderDragEnd,
    draggedFolderId,
    handlePromptCreateFolder,
    creatingFolder,
    tags,
    handleTagCreate,
    correspondents,
    handleCorrespondentCreate,
    appStatus,
    previewActive,
    handleLogout,
    status,
    tenantName,
    tenantOptions,
    currentTenantId,
    handleTenantSelect,
    openSettings,
    handleFileSelection,
    uploadQueue,
  });

  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,
      previewDocumentId,
      closeDocumentPreview,
      handleThumbnailRegeneration,
      documentsTableProps,
      detailPanelProps,
      documentsViewMode,
      ensureAssetUrl,
      resolveFolderPath,
      getDocumentAsset,
      notifyApiError,
      openTagsModal,
      openCorrespondentsModal,
      openSettings,
      detailPanelOpen,
      openDetailPanel,
      uploadQueue,
      clearUploadQueue,
      documentsFilter,
    }),
    [
      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,
      previewDocumentId,
      closeDocumentPreview,
      handleThumbnailRegeneration,
      documentsTableProps,
      detailPanelProps,
      documentsViewMode,
      ensureAssetUrl,
      resolveFolderPath,
      getDocumentAsset,
      notifyApiError,
      openTagsModal,
      openCorrespondentsModal,
      openSettings,
      detailPanelOpen,
      openDetailPanel,
      uploadQueue,
      clearUploadQueue,
      documentsFilter,
    ],
  );

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

export default useDocumentsWorkspace;
