import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  Navigate,
  Outlet,
  matchPath,
  useLocation,
  useMatch,
  useNavigate,
} from 'react-router-dom';
import AssetManager, {
  getAssetFromVersion,
  resolveDocumentAssetUrl,
  createAssetView,
} from '../asset_manager';
import useApiError from '../hooks/useApiError';
import TagManager from '../tag_manager';
import usePasskeys from '../settings/usePasskeys';
import { AppShellContext } from '../appShellContext';
import DropOverlay from './DropOverlay';
import { useManagementModals } from './useManagementModals';
import { api, useAppDispatch, useAppState } from './appState';
import { useDetailPanel } from './useDetailPanel';
import { useDocumentSelection } from './useDocumentSelection';
import { useEntryPointerHandler } from '../documents/useEntryPointer';
import { isTagTransferEvent } from '../documents/tagTransfer';

const ASSET_PRESIGN_TTL_MS = 240 * 1000; // backend issues 5 min tokens; refresh slightly early

const DEFAULT_FOLDER_NAME = 'Documents';

const ROW_KEY_SEPARATOR = ':';
const DOCUMENT_ROW_PREFIX = 'document';
const FOLDER_ROW_PREFIX = 'folder';

const resolveApiPath = (path = '') => path;

const makeRowKey = (type, id) =>
  id ? `${type}${ROW_KEY_SEPARATOR}${id}` : `${type}${ROW_KEY_SEPARATOR}`;

const getRowType = (key) => (typeof key === 'string' ? key.split(ROW_KEY_SEPARATOR, 1)[0] : '');

const getRowId = (key) => {
  if (typeof key !== 'string') return '';
  const separatorIndex = key.indexOf(ROW_KEY_SEPARATOR);
  if (separatorIndex === -1) return key;
  return key.slice(separatorIndex + 1);
};

const isDocumentRowKey = (key) => getRowType(key) === DOCUMENT_ROW_PREFIX;
const isFolderRowKey = (key) => getRowType(key) === FOLDER_ROW_PREFIX;

const resolveDocumentRowKey = (documentId) =>
  documentId ? makeRowKey(DOCUMENT_ROW_PREFIX, documentId) : null;

const resolveFolderRowKey = (folderId) =>
  folderId ? makeRowKey(FOLDER_ROW_PREFIX, folderId) : null;

const hasFiles = (event) =>
  Array.from(event.dataTransfer?.types || []).includes('Files');

const isAssetEquivalent = (lhs, rhs) => {
  if (!lhs || !rhs) return false;
  const lhsView = createAssetView(lhs);
  const rhsView = createAssetView(rhs);
  const lhsPrimaryMetadata = lhsView.getPrimaryMetadata() || lhs?.metadata;
  const rhsPrimaryMetadata = rhsView.getPrimaryMetadata() || rhs?.metadata;
  const lhsCardinality = lhsView.getCardinality() || lhs?.cardinality || null;
  const rhsCardinality = rhsView.getCardinality() || rhs?.cardinality || null;
  const lhsObjects = lhsView.getObjects();
  const rhsObjects = rhsView.getObjects();
  const objectsComparable =
    lhsObjects.length === rhsObjects.length
    && lhsObjects.every((entry, index) => {
      const other = rhsObjects[index];
      if (!other) return false;
      if (entry.ordinal !== other.ordinal) return false;
      if (entry.url && other.url && entry.url === other.url) {
        return true;
      }
      if (!entry.url && !other.url) {
        return JSON.stringify(entry.metadata || null) === JSON.stringify(other.metadata || null);
      }
      return entry.url === other.url;
    });
  return (
    lhs.id === rhs.id
    && lhs.url === rhs.url
    && lhsPrimaryMetadata?.width === rhsPrimaryMetadata?.width
    && lhsPrimaryMetadata?.height === rhsPrimaryMetadata?.height
    && lhs.mime_type === rhs.mime_type
    && lhs.asset_type === rhs.asset_type
    && lhs.updated_at === rhs.updated_at
    && lhsCardinality === rhsCardinality
    && objectsComparable
  );
};

const mergeAssetIntoGroup = (group, assetData) => {
  if (!assetData || !assetData.asset_type) {
    if (Array.isArray(group)) {
      return group;
    }
    return group || {};
  }

  if (Array.isArray(group) || !group) {
    const list = Array.isArray(group) ? group : [];
    const index = list.findIndex((item) => item?.id === assetData.id);
    if (index >= 0) {
      const existing = list[index];
      if (isAssetEquivalent(existing, assetData)) {
        return list;
      }
      const next = list.slice();
      next[index] = { ...existing, ...assetData };
      return next;
    }
    return list.concat({ ...assetData });
  }

  const key = assetData.asset_type;
  const previous = group?.[key];
  if (previous && isAssetEquivalent(previous, assetData)) {
    return group;
  }

  const next = { ...(group || {}) };
  next[key] = { ...(previous || {}), ...assetData };
  return next;
};

const mergeAssetIntoDocument = (doc, assetData) => {
  if (!doc) return doc;
  const existingGroup = doc.current_version?.assets || null;
  const nextGroup = mergeAssetIntoGroup(existingGroup, assetData);
  if (nextGroup === existingGroup) {
    return doc;
  }
  const updatedCurrentVersion = doc.current_version
    ? { ...doc.current_version, assets: nextGroup }
    : { assets: nextGroup };
  return { ...doc, current_version: updatedCurrentVersion };
};

const createRootNode = () => ({
  id: 'root',
  name: DEFAULT_FOLDER_NAME,
  parentId: null,
  children: [],
  expanded: true,
  loaded: false,
  hasChildren: false,
});

const AppLayout = () => {
  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: tenantOptions = [] } = appState;
  const tenantName = tenant?.name || tenant?.slug || null;
  const currentTenantId = tenant?.id || null;
  const [status, setStatus] = useState(null);
  const setStatusMessage = useCallback((message, variant = 'info') => {
    setStatus(message ? { message, variant } : null);
  }, []);
  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 [folderNodes, setFolderNodes] = useState(() => {
    const rootNode = createRootNode();
    return new Map([[rootNode.id, rootNode]]);
  });
  const [folderContents, setFolderContents] = useState(() => new Map());
  const [selectedFolder, setSelectedFolder] = useState(routeFolderId || 'root');
  const [currentFolder, setCurrentFolder] = useState(null);
  const [currentSubfolders, setCurrentSubfolders] = useState([]);
  const [documents, setDocuments] = useState([]);
  const [documentsViewMode, setDocumentsViewMode] = useState(() => {
    if (typeof window === 'undefined') {
      return 'list';
    }
    try {
      const stored = window.sessionStorage.getItem('papercrate_view_mode');
      return stored === 'grid' || stored === 'desk' ? stored : 'list';
    } catch (error) {
      console.warn('[view-mode] failed to read stored mode', error);
      return 'list';
    }
  });
  const [deskHelpOpen, setDeskHelpOpen] = useState(false);
  const lastNonDeskViewRef = useRef(documentsViewMode === 'desk' ? 'list' : documentsViewMode);

  useEffect(() => {
    if (documentsViewMode !== 'desk') {
      lastNonDeskViewRef.current = documentsViewMode;
    }
  }, [documentsViewMode]);

  useEffect(() => {
    if (documentsViewMode !== 'desk' && deskHelpOpen) {
      setDeskHelpOpen(false);
    }
  }, [documentsViewMode, deskHelpOpen]);
  const initialRowSelection = [];
  const tokenRef = useRef(token);
  const refreshPromiseRef = useRef(null);
  const breadcrumbFetchRef = useRef(new Set());
  const tagRemovalCursorActiveRef = useRef(false);
  const tenantIdRef = useRef(currentTenantId);
  const detailPanelControlRef = useRef({ open: () => {}, close: () => {} });
  const setTagRemovalCursor = useCallback((active) => {
    if (typeof document === 'undefined') {
      return;
    }
    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 refreshAccessToken = useCallback(async () => {
    console.log('[Auth] Attempting to refresh access token…');
    appDispatch({ type: 'TOKEN_REFRESH_START' });
    try {
      const { data } = await api.post('/auth/refresh');
      if (data?.access_token) {
        appDispatch({
          type: 'TOKEN_REFRESH_SUCCESS',
          token: data.access_token,
          tenant: data.tenant || null,
        });
        console.log('[Auth] Access token refreshed at', new Date().toISOString());
        return data.access_token;
      }
      throw new Error('Missing access token in refresh response');
    } catch (error) {
      console.warn('[Auth] Failed to refresh access token', error);
      appDispatch({ type: 'TOKEN_REFRESH_FAILURE', error: error?.message || null });
      throw error;
    }
  }, [appDispatch]);
  const [searchResults, setSearchResults] = useState(null);
  const [previewEntries, setPreviewEntries] = useState(() => new Map());
  const previewInflightRef = useRef(new Map());
  const previewReturnPathRef = useRef(null);
  const [tags, setTags] = useState([]);
  const [correspondents, setCorrespondents] = useState([]);
  const [searchQuery, setSearchQuery] = useState('');
  const [activeTagFilters, setActiveTagFilters] = useState([]);
  const [activeCorrespondentFilters, setActiveCorrespondentFilters] = useState([]);
  const [searchLoading, setSearchLoading] = useState(false);
  const documentsRouteMatch = useMatch('/documents');
  const documentsFolderRouteMatch = useMatch('/documents/folder/:folderId');
  const documentsDetailRouteMatch = useMatch('/documents/:documentId');
  const isDocumentsRoute = Boolean(
    documentsRouteMatch || documentsFolderRouteMatch || documentsDetailRouteMatch,
  );
  const toggleTagFilter = useCallback((tagId) => {
    if (!tagId) return;
    setActiveTagFilters((previous) =>
      previous.includes(tagId)
        ? previous.filter((id) => id !== tagId)
        : previous.concat([tagId]),
    );
  }, []);

  const toggleCorrespondentFilter = useCallback((correspondentId) => {
    setActiveCorrespondentFilters((previous) => {
      if (!correspondentId) {
        return [];
      }
      return previous.includes(correspondentId) ? [] : [correspondentId];
    });
  }, []);

  const initialRefreshAttemptedRef = useRef(Boolean(token));

  useEffect(() => {
    if (!token && !initialRefreshAttemptedRef.current && appStatus === 'logged-out') {
      initialRefreshAttemptedRef.current = true;
      console.log('[Auth] Attempting refresh at startup');
      refreshAccessToken().catch(() => {});
    }
  }, [token, appStatus, refreshAccessToken]);

  const clearFilters = useCallback(() => {
    setSearchQuery('');
    setActiveTagFilters([]);
    setActiveCorrespondentFilters([]);
    setSearchLoading(false);
  }, []);

  const handleSearchChange = useCallback((value) => {
    setSearchQuery(value);
  }, []);

  const handleSearchSubmit = useCallback(() => {
    if (!navigate) return;
    const targetFolder = selectedFolder && selectedFolder !== 'root' ? selectedFolder : 'root';
    const targetPath = targetFolder === 'root' ? '/documents' : `/documents/folder/${targetFolder}`;
    if (!isDocumentsRoute || location.pathname !== targetPath) {
      navigate(targetPath, { replace: false });
    }
  }, [navigate, selectedFolder, isDocumentsRoute, location.pathname]);
  const [draggedDocumentIds, setDraggedDocumentIds] = useState([]);
  const [draggedFolderId, setDraggedFolderId] = useState(null);
  const [dropOverlayState, setDropOverlayState] = useState({
    active: false,
    folderName: DEFAULT_FOLDER_NAME,
  });
  const [activePreviewId, setActivePreviewId] = useState(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 {
    selectedEntries,
    setSelectedEntries,
    selectionOrder,
    setSelectionOrder,
    selectionOrderRef,
    selectionAnchorRef,
    selectionInitializedRef,
    focusedDocumentId,
    setFocusedDocumentId,
    focusedRowKey,
    setFocusedRowKey,
    applySelection,
    clearSelection: clearSelectionInternal,
    handleEntrySelection: handleEntrySelectionInternal,
    promoteSelectionOrder: promoteSelectionOrderInternal,
    configureSelectionEnvironment,
  } = useDocumentSelection({
    resolveDocumentRowKey,
    resolveFolderRowKey,
    isDocumentRowKey,
    isFolderRowKey,
    getRowId,
    initialEntries: initialRowSelection,
  });

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

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

  const selectedDocumentIds = useMemo(
    () =>
      selectedEntries
        .filter(isDocumentRowKey)
        .map((key) => getRowId(key))
        .filter(Boolean),
    [selectedEntries],
  );

  const selectedFolderIds = useMemo(
    () =>
      selectedEntries
        .filter(isFolderRowKey)
        .map((key) => getRowId(key))
        .filter(Boolean),
    [selectedEntries],
  );


  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([]);
    setDropOverlayState({ active: false, folderName: DEFAULT_FOLDER_NAME });
    setActivePreviewId(null);
    detailPanelControlRef.current.close();
    assetManager.reset();
    setPreviewEntries(() => new Map());
    previewInflightRef.current = new Map();
    dragCounterRef.current = 0;
    breadcrumbFetchRef.current = new Set();
    detailFolderFetchRef.current = new Set();
    bootstrapInitializedRef.current = false;
    selectionInitializedRef.current = false;
    tenantIdRef.current = null;
  }, [
    assetManager,
    selectionAnchorRef,
    selectionInitializedRef,
    selectionOrderRef,
    setFocusedDocumentId,
    setSelectedEntries,
    setSelectionOrder,
  ]);

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

  const correspondentLookupByName = useMemo(() => {
    const map = new Map();
    correspondents.forEach((correspondent) => {
      if (correspondent?.name) {
        map.set(correspondent.name.toLowerCase(), correspondent);
      }
    });
    return map;
  }, [correspondents]);
  useEffect(() => {
    if (appStatus === 'logged-out' || appStatus === 'selecting-tenant') {
      resetWorkspaceState();
    }
  }, [appStatus, resetWorkspaceState]);

  useEffect(() => {
    tokenRef.current = token;
  }, [token]);

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

  useEffect(() => {
    const requestInterceptor = api.interceptors.request.use((config) => {
      const currentToken = tokenRef.current;
      if (currentToken) {
        config.headers = config.headers || {};
        if (!config.headers.Authorization) {
          config.headers.Authorization = `Bearer ${currentToken}`;
        }
      }
      return config;
    });

    const responseInterceptor = api.interceptors.response.use(
      (response) => response,
      async (error) => {
        const { response, config } = error;
        if (!response || !config) {
          return Promise.reject(error);
        }

        const status = response.status;
        const url = typeof config.url === 'string' ? config.url : '';
        const isAuthRoute = url.includes('/auth/login') || url.includes('/auth/refresh');

        if (status === 401 && !config._retry && !isAuthRoute) {
          console.warn('[Auth] 401 received for', url, '- attempting token refresh');

          if (!refreshPromiseRef.current) {
            refreshPromiseRef.current = (async () => {
              try {
                return await refreshAccessToken();
              } finally {
                refreshPromiseRef.current = null;
              }
            })();
          }

          try {
            const newToken = await refreshPromiseRef.current;
            if (!newToken) {
              throw new Error('No token returned from refresh');
            }
            config._retry = true;
            config.headers = config.headers || {};
            config.headers.Authorization = `Bearer ${newToken}`;
            console.log('[Auth] Retrying original request', url);
            try {
              return await api(config);
            } catch (retryError) {
              if (retryError?.response?.status === 401) {
                notifyApiError(retryError, 'Session expired. Please log in again.');
              }
              throw retryError;
            }
          } catch (refreshError) {
            console.warn('[Auth] Refresh failed, clearing session');
            notifyApiError(refreshError, 'Session expired. Please log in again.');
            return Promise.reject(refreshError);
          }
        }

        return Promise.reject(error);
      },
    );

    return () => {
      api.interceptors.request.eject(requestInterceptor);
      api.interceptors.response.eject(responseInterceptor);
    };
  }, [notifyApiError, refreshAccessToken]);

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

  const currentFolderName = useMemo(() => {
    if (selectedFolder === 'root' || !currentFolder) return DEFAULT_FOLDER_NAME;
    return currentFolder.name;
  }, [selectedFolder, currentFolder]);

  const isFilterActive = useMemo(
    () =>
      searchQuery.trim().length > 0 ||
      activeTagFilters.length > 0 ||
      activeCorrespondentFilters.length > 0,
    [searchQuery, activeTagFilters, activeCorrespondentFilters],
  );

  const applySelectedFolder = useCallback(
    (folderId, contents) => {
      const subfolders = contents?.subfolders ?? [];
      const docs = assetManager.hydrateDocuments(contents?.documents ?? []);
      const folderInfo = contents?.folder ?? null;

      setCurrentSubfolders(subfolders);
      setDocuments(docs);
      setCurrentFolder(folderInfo);

      const availableDocKeys = docs
        .map((doc) => resolveDocumentRowKey(doc.id))
        .filter(Boolean);
      const availableDocKeySet = new Set(availableDocKeys);
      const availableFolderKeys = new Set(
        subfolders
          .map((folder) => resolveFolderRowKey(folder.id))
          .filter(Boolean),
      );

      let nextDocKeys = [];
      let mergedSelection = [];

      setSelectedEntries((previous) => {
        const previousFolderKeys = previous
          .filter(isFolderRowKey)
          .filter((key) => availableFolderKeys.has(key));
        const previousDocKeys = previous.filter(isDocumentRowKey);

        if (selectionInitializedRef.current) {
          nextDocKeys = previousDocKeys.filter((key) => availableDocKeySet.has(key));
        } else {
          nextDocKeys = previousDocKeys.filter((key) => availableDocKeySet.has(key));
        }

        mergedSelection = [...previousFolderKeys, ...nextDocKeys];
        return mergedSelection;
      });

      const nextFocus = (() => {
        const currentFocusedKey = resolveDocumentRowKey(focusedDocumentId);
        if (currentFocusedKey && availableDocKeySet.has(currentFocusedKey)) {
          return focusedDocumentId;
        }
        if (nextDocKeys.length) {
          const lastDocKey = nextDocKeys[nextDocKeys.length - 1];
          return getRowId(lastDocKey) || null;
        }
        return null;
      })();

      setFocusedDocumentId(nextFocus);
      selectionAnchorRef.current = nextDocKeys.length
        ? nextDocKeys[nextDocKeys.length - 1]
        : null;
      selectionOrderRef.current = mergedSelection;
      setSelectionOrder(mergedSelection);

      return nextFocus;
    },
    [
      assetManager,
      focusedDocumentId,
      selectionAnchorRef,
      selectionInitializedRef,
      selectionOrderRef,
      setFocusedDocumentId,
      setSelectedEntries,
      setSelectionOrder,
    ],
  );

  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);
    }
    return map;
  }, [documents, searchResults]);

  const mapDocumentCaches = useCallback(
    (mapper) => {
      if (typeof mapper !== 'function') {
        return;
      }

      const applyToList = (list) => {
        let changed = false;
        const next = list.map((doc) => {
          const updated = mapper(doc);
          if (updated === undefined || updated === doc) {
            return doc;
          }
          changed = true;
          return updated;
        });
        return changed ? next : list;
      };

      setDocuments((prev) => applyToList(prev));
      setSearchResults((prev) => {
        if (!Array.isArray(prev)) {
          return prev;
        }
        return applyToList(prev);
      });
      setFolderContents((prev) => {
        if (!prev.size) {
          return prev;
        }
        let changed = false;
        const next = new Map();
        prev.forEach((contents, key) => {
          const docs = Array.isArray(contents?.documents) ? contents.documents : null;
          if (!docs || docs.length === 0) {
            next.set(key, contents);
            return;
          }
          let docsChanged = false;
          const updatedDocs = docs.map((doc) => {
            const updated = mapper(doc);
            if (updated === undefined || updated === doc) {
              return doc;
            }
            docsChanged = true;
            return updated;
          });
          if (docsChanged) {
            changed = true;
            next.set(key, { ...contents, documents: updatedDocs });
          } else {
            next.set(key, contents);
          }
        });
        return changed ? next : prev;
      });
    },
    [setDocuments, setSearchResults, setFolderContents],
  );

  const updateDocumentCaches = useCallback(
    (documentId, updater) => {
      if (!documentId || typeof updater !== 'function') {
        return;
      }

      mapDocumentCaches((doc) => {
        if (!doc || doc.id !== documentId) {
          return doc;
        }
        const updated = updater(doc);
        return updated === undefined ? doc : updated;
      });
    },
    [mapDocumentCaches],
  );

  const removeDocumentsFromCaches = useCallback(
    (documentIds) => {
      if (!documentIds || documentIds.length === 0) {
        return;
      }

      const idSet = new Set(documentIds);

      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) => {
        if (!prev.size) {
          return prev;
        }
        let changed = false;
        const next = new Map();
        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;
      });

      setPreviewEntries((prev) => {
        if (!prev.size) {
          return prev;
        }
        let changed = false;
        const next = new Map(prev);
        idSet.forEach((id) => {
          if (next.delete(id)) {
            changed = true;
          }
        });
        return changed ? next : prev;
      });

      idSet.forEach((id) => {
        previewInflightRef.current.delete(id);
      });
    },
    [setDocuments, setSearchResults, setFolderContents, setPreviewEntries],
  );

  const folderOptions = useMemo(() => {
    const cache = new Map();
    const computePath = (id) => {
      if (cache.has(id)) {
        return cache.get(id);
      }
      if (!id || id === 'root') {
        cache.set('root', DEFAULT_FOLDER_NAME);
        return DEFAULT_FOLDER_NAME;
      }
      const node = folderNodes.get(id);
      if (!node) {
        return 'Folder';
      }
      const parentId = node.parentId || 'root';
      const parentPath = computePath(parentId);
      const name = node.name || 'Folder';
      const fullPath = parentId === 'root' ? name : `${parentPath}/${name}`;
      cache.set(id, fullPath);
      return fullPath;
    };

    const entries = [];
    folderNodes.forEach((node, id) => {
      if (!node) return;
      entries.push({ id, label: computePath(id) });
    });

    entries.sort((a, b) => {
      if (a.id === 'root') return -1;
      if (b.id === 'root') return 1;
      return a.label.localeCompare(b.label, undefined, { sensitivity: 'base' });
    });

    return entries;
  }, [folderNodes]);

  const folderLabelMap = useMemo(() => {
    const map = new Map();
    folderOptions.forEach((option) => {
      map.set(option.id, option.label);
    });
    return map;
  }, [folderOptions]);

  const navigableRows = useMemo(() => {
    const entries = [];
    if (!showingSearchResults) {
      currentSubfolders.forEach((folder) => {
        const key = resolveFolderRowKey(folder.id);
        if (key) {
          entries.push({ key, type: 'folder', id: folder.id });
        }
      });
    }
    visibleDocuments.forEach((doc) => {
      const key = resolveDocumentRowKey(doc.id);
      if (key) {
        entries.push({ key, type: 'document', id: doc.id });
      }
    });
    return entries;
  }, [showingSearchResults, currentSubfolders, visibleDocuments]);

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

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

  const handleEntrySelection = useCallback(
    (rowKey, event) => {
      handleEntrySelectionInternal(rowKey, event);
    },
    [handleEntrySelectionInternal],
  );

  const promoteSelectionOrder = useCallback(
    (docId) => {
      if (!docId) return;
      promoteSelectionOrderInternal(docId);
      const rowKey = resolveDocumentRowKey(docId);
      if (rowKey) {
        selectionAnchorRef.current = rowKey;
      }
      setFocusedDocumentId(docId);
      setActivePreviewId(docId);
    },
    [
      promoteSelectionOrderInternal,
      selectionAnchorRef,
      setFocusedDocumentId,
      setActivePreviewId,
    ],
  );

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

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

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

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

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

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

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


  const ensureFolderData = useCallback(
    async (
      folderId,
      { force = false, includeDocuments = true, prefetchDepth = 0 } = {},
    ) => {
      const requestTenantId = tenantIdRef.current;
      const cached = folderContents.get(folderId);
      if (!force && cached) {
        const includesDocuments = Boolean(cached.__includesDocuments);
        if (!includeDocuments || includesDocuments) {
          if (prefetchDepth > 0) {
            const subfolders = Array.isArray(cached.subfolders) ? cached.subfolders : [];
            await Promise.allSettled(
              subfolders.map((entry) =>
                ensureFolderData(entry.id, {
                  includeDocuments: false,
                  prefetchDepth: prefetchDepth - 1,
                  force: false,
                }),
              ),
            );
          }
          return cached;
        }
      }

      const path = folderId === 'root' ? 'root' : folderId;
      const params = includeDocuments
        ? undefined
        : { include_documents: false };
      const { data } = await api.get(`/folders/${path}/contents`, {
        params,
      });
      const hydrated = assetManager.hydrateFolderContents(data);
      const childFolders = Array.isArray(data.subfolders) ? data.subfolders : [];
      const childIds = childFolders.map((child) => child.id);

      if (tenantIdRef.current !== requestTenantId) {
        return { ...hydrated, __includesDocuments: includeDocuments };
      }

      setFolderNodes((prev) => {
        const next = new Map(prev);
        const existingNode = next.get(folderId) || {
          id: folderId,
          name: folderId === 'root' ? DEFAULT_FOLDER_NAME : data.folder?.name || 'Folder',
          parentId: data.folder?.parent_id || 'root',
          children: [],
          expanded: folderId === 'root',
          loaded: false,
          hasChildren: false,
        };

        next.set(folderId, {
          ...existingNode,
          name: folderId === 'root' ? DEFAULT_FOLDER_NAME : data.folder?.name || existingNode.name,
          parentId: data.folder?.parent_id ?? existingNode.parentId ?? 'root',
          children: childIds,
          expanded: folderId === 'root' ? true : existingNode.expanded,
          loaded: true,
          hasChildren: childIds.length > 0,
        });

        childFolders.forEach((child) => {
          const childNode = next.get(child.id);
          const previousChildren = Array.isArray(childNode?.children) ? childNode.children : [];
          const childHasChildren = (() => {
            if (childNode?.loaded) {
              return previousChildren.length > 0;
            }
            if (Array.isArray(child?.subfolders)) {
              return child.subfolders.length > 0;
            }
            if (typeof child?.has_children === 'boolean') {
              return child.has_children;
            }
            if (typeof child?.hasChildren === 'boolean') {
              return child.hasChildren;
            }
            if (typeof childNode?.hasChildren === 'boolean') {
              return childNode.hasChildren;
            }
            return false;
          })();
          next.set(child.id, {
            id: child.id,
            name: child.name,
            parentId: child.parent_id ?? 'root',
            children: previousChildren,
            expanded: childNode?.expanded ?? false,
            loaded: childNode?.loaded ?? false,
            hasChildren: childHasChildren,
          });
        });

        return next;
      });

      if (prefetchDepth > 0 && childIds.length > 0 && tenantIdRef.current === requestTenantId) {
        await Promise.allSettled(
          childIds.map((childId) =>
            ensureFolderData(childId, {
              includeDocuments: false,
              force: false,
              prefetchDepth: prefetchDepth - 1,
            }),
          ),
        );
      }

      const enriched = {
        ...hydrated,
        __includesDocuments: includeDocuments,
      };

      if (includeDocuments) {
        setFolderContents((prev) => {
          if (tenantIdRef.current !== requestTenantId) {
            return prev;
          }
          const next = new Map(prev);
          next.set(folderId, enriched);
          return next;
        });
      } else {
        setFolderContents((prev) => {
          if (tenantIdRef.current !== requestTenantId) {
            return prev;
          }
          const next = new Map(prev);
          const existingEntry = next.get(folderId);
          if (existingEntry) {
            next.set(folderId, {
              ...existingEntry,
              ...hydrated,
              documents: existingEntry.__includesDocuments
                ? existingEntry.documents
                : hydrated.documents,
              __includesDocuments: existingEntry.__includesDocuments || false,
            });
          } else {
            next.set(folderId, enriched);
          }
          return next;
        });
      }

      return enriched;
    },
    [assetManager, folderContents],
  );

  const isInvalidFolderDrop = useCallback(
    (sourceId, targetId) => {
      if (!sourceId) return false;
      if (!targetId || targetId === 'root') {
        return false;
      }
      if (sourceId === targetId) {
        return true;
      }

      let current = targetId;
      const visited = new Set();
      while (current && current !== 'root' && !visited.has(current)) {
        visited.add(current);
        if (current === sourceId) {
          return true;
        }
        const node = folderNodes.get(current);
        if (!node) break;
        current = node.parentId ?? 'root';
      }
      return false;
    },
    [folderNodes],
  );

  const moveFolder = useCallback(
    async (folderId, targetFolderId) => {
      const node = folderNodes.get(folderId);
      if (!node) {
        setStatusMessage('Folder metadata unavailable. Try refreshing.', 'error');
        return;
      }

      const previousParentKey = node.parentId ?? 'root';
      const targetKey = targetFolderId && targetFolderId !== 'root' ? targetFolderId : 'root';

      if (previousParentKey === targetKey) {
        return;
      }

      const parent_id = targetKey === 'root' ? null : targetKey;

      try {
        await api.patch(`/folders/${folderId}`, { parent_id });

        setFolderNodes((prev) => {
          const next = new Map(prev);
          const currentNode = next.get(folderId);
          if (!currentNode) {
            return prev;
          }

          const updatedNode = { ...currentNode, parentId: parent_id ?? null };
          next.set(folderId, updatedNode);

          const previousParent = next.get(previousParentKey);
          if (previousParent) {
            const remainingChildren = (previousParent.children || []).filter(
              (childId) => childId !== folderId,
            );
            next.set(previousParentKey, {
              ...previousParent,
              children: remainingChildren,
              hasChildren: remainingChildren.length > 0,
            });
          }

          if (!next.has(targetKey)) {
            next.set(targetKey, {
              id: targetKey,
              name: targetKey === 'root' ? DEFAULT_FOLDER_NAME : 'Folder',
              parentId: targetKey === 'root' ? null : null,
              children: [],
              expanded: targetKey === 'root',
              loaded: false,
              hasChildren: false,
            });
          }

          const targetNode = next.get(targetKey);
          if (targetNode && !targetNode.children.includes(folderId)) {
            next.set(targetKey, {
              ...targetNode,
              children: [...targetNode.children, folderId],
              hasChildren: true,
            });
          }

          return next;
        });

        const refreshTargets = new Set([previousParentKey, targetKey]);
        for (const key of refreshTargets) {
          if (key === 'root') {
            await ensureFolderData('root', { force: true, prefetchDepth: 1 });
          } else {
            await ensureFolderData(key, { force: true, prefetchDepth: 1 });
          }
        }

        if (selectedFolder === folderId) {
          await ensureFolderData(folderId, { force: true, prefetchDepth: 1 });
          setSelectedFolder(folderId);
        }

        setStatusMessage('Folder moved.', 'success');
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to move folder.';
        notifyApiError(error, message);

        const refreshTargets = new Set([previousParentKey, targetKey]);
        for (const key of refreshTargets) {
          if (key === 'root') {
            await ensureFolderData('root', { force: true, prefetchDepth: 1 });
          } else {
            await ensureFolderData(key, { force: true, prefetchDepth: 1 });
          }
        }
      }
    },
    [
      folderNodes,
      ensureFolderData,
      selectedFolder,
      setSelectedFolder,
      setFolderNodes,
      notifyApiError,
      setStatusMessage,
    ],
  );

  const refreshTags = useCallback(async () => {
    const requestTenantId = tenantIdRef.current;
    try {
      const { data } = await api.get('/tags');
      if (tenantIdRef.current !== requestTenantId) {
        return;
      }
      setTags(data || []);
    } catch (error) {
      if (tenantIdRef.current !== requestTenantId) {
        return;
      }
      notifyApiError(error, 'Unable to load tags.');
    }
  }, [notifyApiError]);

  const refreshCorrespondents = useCallback(async () => {
    const requestTenantId = tenantIdRef.current;
    try {
      const { data } = await api.get('/correspondents');
      if (tenantIdRef.current !== requestTenantId) {
        return;
      }
      setCorrespondents(data || []);
    } catch (error) {
      if (tenantIdRef.current !== requestTenantId) {
        return;
      }
      notifyApiError(error, 'Unable to load correspondents.');
    }
  }, [notifyApiError]);

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

  const handleTagUpdate = useCallback(
    async (tagId, changes) => {
      if (!tagId) {
        throw new Error('Missing tag identifier.');
      }

      const payload = {};
      if (typeof changes.label === 'string') {
        payload.label = changes.label;
      }
      if (Object.prototype.hasOwnProperty.call(changes, 'color')) {
        payload.color = changes.color;
      }

      if (Object.keys(payload).length === 0) {
        return false;
      }

      try {
        await api.patch(`/tags/${tagId}`, payload);
        await refreshTags();
        setStatusMessage('Tag updated.', 'success');
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to update tag.';
        notifyApiError(error, message);
        throw new Error(message);
      }
    },
    [refreshTags, notifyApiError, setStatusMessage],
  );

  const handleTagCreate = useCallback(
    async ({ label, color } = {}) => {
      const payload = tagManager.buildPayload({ label, color });
      try {
        await api.post('/tags', payload);
        await refreshTags();
        setStatusMessage('Tag created.', 'success');
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to create tag.';
        notifyApiError(error, message);
        throw new Error(message);
      }
    },
    [refreshTags, notifyApiError, setStatusMessage, tagManager],
  );

  const handleCorrespondentUpdate = useCallback(
    async (correspondentId, changes) => {
      if (!correspondentId) {
        throw new Error('Missing correspondent identifier.');
      }

      const payload = {};
      if (typeof changes.name === 'string') {
        const trimmed = changes.name.trim();
        if (!trimmed) {
          throw new Error('Correspondent name cannot be empty.');
        }
        payload.name = trimmed;
      }

      if (Object.keys(payload).length === 0) {
        return false;
      }

      try {
        await api.patch(`/correspondents/${correspondentId}`, payload);
        await refreshCorrespondents();
        setStatusMessage('Correspondent updated.', 'success');
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to update correspondent.';
        notifyApiError(error, message);
        throw new Error(message);
      }
    },
    [refreshCorrespondents, notifyApiError, setStatusMessage],
  );

  const handleCorrespondentCreate = useCallback(
    async ({ name }) => {
      const trimmed = typeof name === 'string' ? name.trim() : '';
      if (!trimmed) {
        throw new Error('Correspondent name is required.');
      }
      try {
        const { data } = await api.post('/correspondents', { name: trimmed });
        await refreshCorrespondents();
        setStatusMessage('Correspondent created.', 'success');
        return data;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to create correspondent.';
        notifyApiError(error, message);
        throw new Error(message);
      }
    },
    [refreshCorrespondents, notifyApiError, setStatusMessage],
  );

  const handleCorrespondentDelete = useCallback(
    async (correspondentId) => {
      if (!correspondentId) {
        throw new Error('Missing correspondent identifier.');
      }

      const stripFromDoc = (doc) => {
        if (!doc || !Array.isArray(doc.correspondents)) {
          return doc;
        }
        const next = doc.correspondents.filter((entry) => entry.id !== correspondentId);
        if (next.length === doc.correspondents.length) {
          return doc;
        }
        return { ...doc, correspondents: next };
      };

      try {
        await api.delete(`/correspondents/${correspondentId}`);
        await refreshCorrespondents();

        mapDocumentCaches(stripFromDoc);

        setStatusMessage('Correspondent deleted.', 'success');
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to delete correspondent.';
        notifyApiError(error, message);
        throw new Error(message);
      }
    },
    [refreshCorrespondents, notifyApiError, setStatusMessage, mapDocumentCaches],
  );

  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 handleDocumentCorrespondentAttach = useCallback(
    async ({ documentId, correspondentId }, { notify = true, refresh = true } = {}) => {
      if (!documentId || !correspondentId) {
        throw new Error('Missing document or correspondent.');
      }
      try {
        await api.post(`/documents/${documentId}/correspondents`, {
          assignments: [{ correspondent_id: correspondentId }],
          replace: false,
        });
        if (refresh) {
          await refreshCurrentFolder();
        }
        if (notify) {
          setStatusMessage('Correspondent assigned.', 'success');
        }
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to assign correspondent.';
        notifyApiError(error, message);
        throw new Error(message);
      }
    },
    [notifyApiError, refreshCurrentFolder, setStatusMessage],
  );

  const handleCorrespondentRemove = useCallback(
    async ({ documentId, correspondentId }, { notify = true, refresh = true } = {}) => {
      if (!documentId || !correspondentId) {
        throw new Error('Missing document or correspondent.');
      }
      try {
        await api.delete(`/documents/${documentId}/correspondents/${correspondentId}`);
        if (refresh) {
          await refreshCurrentFolder();
        }
        if (notify) {
          setStatusMessage('Correspondent removed.', 'success');
        }
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to remove correspondent.';
        notifyApiError(error, message);
        throw new Error(message);
      }
    },
    [notifyApiError, refreshCurrentFolder, setStatusMessage],
  );

  const handleCorrespondentAdd = useCallback(
    async ({ document, name, input = null, option = null }) => {
      if (!document?.id) {
        throw new Error('Missing document for correspondent assignment.');
      }
      const trimmed = typeof name === 'string' ? name.trim() : '';
      if (!trimmed) {
        setStatusMessage('Correspondent name is required.', 'error');
        return;
      }

      let target = null;
      if (option && option.id) {
        target = correspondentLookupByName.get(trimmed.toLowerCase()) || option;
      } else {
        target = correspondentLookupByName.get(trimmed.toLowerCase()) || null;
      }
      if (!target) {
        try {
          target = await handleCorrespondentCreate({ name: trimmed });
        } catch (error) {
          return;
        }
      }

      if (!target?.id) {
        setStatusMessage('Unable to resolve correspondent.', 'error');
        return;
      }

      try {
        await handleDocumentCorrespondentAttach({
          documentId: document.id,
          correspondentId: target.id,
        });
        if (input) {
          input.value = '';
        }
      } catch (error) {
        setStatusMessage('Failed to assign correspondent.', 'error');
        console.error('[documents] assign correspondent failed', error);
      }
    },
    [
      handleCorrespondentCreate,
      handleDocumentCorrespondentAttach,
      correspondentLookupByName,
      setStatusMessage,
    ],
  );

  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 handleTagDelete = useCallback(
    async (tagId) => {
      if (!tagId) {
        throw new Error('Missing tag identifier.');
      }

      try {
        await api.delete(`/tags/${tagId}`);
        setActiveTagFilters((prev) => prev.filter((id) => id !== tagId));

        const stripTagFromDoc = (doc) => {
          if (!doc || !Array.isArray(doc.tags)) {
            return doc;
          }
          const nextTags = doc.tags.filter((tag) => tag.id !== tagId);
          if (nextTags.length === doc.tags.length) {
            return doc;
          }
          return { ...doc, tags: nextTags };
        };

        mapDocumentCaches(stripTagFromDoc);

        await refreshTags();
        setStatusMessage('Tag deleted.', 'success');
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to delete tag.';
        notifyApiError(error, message);
        throw new Error(message);
      }
    },
    [
      refreshTags,
      notifyApiError,
      setStatusMessage,
      mapDocumentCaches,
      setActiveTagFilters,
    ],
  );

  const expandFolderAncestors = useCallback(
    (targetId) => {
      if (!targetId || targetId === 'root') {
        setFolderNodes((prev) => {
          if (prev.get('root')?.expanded) {
            return prev;
          }
          const next = new Map(prev);
          const rootNode = next.get('root');
          if (rootNode) {
            next.set('root', { ...rootNode, expanded: true });
          }
          return next;
        });
        return;
      }

      setFolderNodes((prev) => {
        const next = new Map(prev);
        let currentId = targetId;
        let guard = 0;
        while (currentId && !next.has(currentId) && guard < 32) {
          guard += 1;
          const node = prev.get(currentId);
          if (!node) {
            break;
          }
          currentId = node.parentId ?? 'root';
        }

        currentId = targetId;
        guard = 0;
        while (currentId && guard < 32) {
          guard += 1;
          const node = next.get(currentId);
          if (!node) {
            break;
          }
          if (!node.expanded && currentId !== targetId) {
            next.set(currentId, { ...node, expanded: true });
          }
          currentId = node.parentId ?? 'root';
          if (!currentId || currentId === 'root') {
            const rootNode = next.get('root');
            if (rootNode && !rootNode.expanded) {
              next.set('root', { ...rootNode, expanded: true });
            }
            break;
          }
        }
        return next;
      });
    },
    []);

  const ensureFolderAncestorsLoaded = useCallback(
    async (targetId) => {
      if (!targetId || targetId === 'root') {
        return;
      }

      const fetchAncestor = async (folderId, guard = 0) => {
        if (!folderId || folderId === 'root' || guard > 32) {
          return;
        }

        const existing = folderNodes.get(folderId);
        if (existing?.loaded) {
          return;
        }

        try {
          const contents = await ensureFolderData(folderId, {
            includeDocuments: false,
            force: false,
            prefetchDepth: 0,
          });
          const parentId = contents?.folder?.parent_id ?? 'root';
          if (parentId && parentId !== 'root') {
            await fetchAncestor(parentId, guard + 1);
          }
        } catch (error) {
          console.warn('Failed to ensure ancestor folder for navigation', folderId, error);
        }
      };

      await fetchAncestor(targetId, 0);
    },
    [folderNodes, ensureFolderData],
  );

  const loadFolder = useCallback(
    async (folderId, { showLoading = true, preserveSearch = false } = {}) => {
      const targetId = folderId || 'root';
      setSelectedFolder(targetId);
      await ensureFolderAncestorsLoaded(targetId);
      expandFolderAncestors(targetId);
      if (showLoading) setLoading(true);
      try {
        const contents = await ensureFolderData(targetId, { force: true, prefetchDepth: 1 });
        if (targetId !== 'root') {
          try {
            await ensureFolderData('root', {
              force: false,
              includeDocuments: false,
              prefetchDepth: 1,
            });
          } catch (error) {
            console.warn('Failed to refresh root folder tree', error);
          }
        }
        applySelectedFolder(targetId, contents);
        if (!preserveSearch) {
          setSearchResults(null);
        }
      } catch (error) {
        notifyApiError(error, 'Failed to load folder contents.');
      } finally {
        if (showLoading) setLoading(false);
      }
    },
    [
      ensureFolderData,
      applySelectedFolder,
      notifyApiError,
      ensureFolderAncestorsLoaded,
      expandFolderAncestors,
    ],
  );

  const selectFolder = useCallback(
    async (folderId, { replace = false, immediate = false } = {}) => {
      const targetId = folderId && folderId !== 'root' ? folderId : 'root';

      await ensureFolderAncestorsLoaded(targetId);
      expandFolderAncestors(targetId);

      if (!navigate || immediate) {
        await loadFolder(targetId, { preserveSearch: isFilterActive });
        setSelectedFolder(targetId);
        return;
      }

      const path = targetId === 'root' ? '/documents' : `/documents/folder/${targetId}`;
      navigate(path, { replace });
    },
    [
      ensureFolderAncestorsLoaded,
      expandFolderAncestors,
      navigate,
      loadFolder,
      isFilterActive,
      setSelectedFolder,
    ],
  );

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

  const handleBulkCorrespondentAdd = useCallback(
    async ({ name, input, documentIds }) => {
      const trimmed = typeof name === 'string' ? name.trim() : '';
      if (!trimmed) {
        setStatusMessage('Correspondent name is required.', 'error');
        return;
      }
      const targets = resolveTargetDocumentIds(documentIds);
      if (!targets.length) {
        setStatusMessage('Select documents before assigning correspondents.', 'error');
        return;
      }

      let target = correspondentLookupByName.get(trimmed.toLowerCase()) || null;
      if (!target) {
        try {
          target = await handleCorrespondentCreate({ name: trimmed });
        } catch (error) {
          return;
        }
      }

      if (!target?.id) {
        setStatusMessage('Unable to resolve correspondent.', 'error');
        return;
      }

      const response = await api.post('/documents/bulk/correspondents', {
        document_ids: targets,
        assignments: [
          {
            correspondent_id: target.id,
          },
        ],
        action: 'add',
      });

      const { assigned = 0, removed = 0 } = response.data || {};

      await refreshCurrentFolder();
      const assignedSuffix = assigned === 1 ? '' : 's';
      if (removed > 0) {
        const removedSuffix = removed === 1 ? '' : 's';
        setStatusMessage(
          `Correspondent assigned (${assigned}) and replaced ${removed} link${removedSuffix}.`,
          'success',
        );
      } else {
        setStatusMessage(
          `Correspondent assigned to ${assigned} document${assignedSuffix}.`,
          'success',
        );
      }

      if (input) {
        input.value = '';
      }
    },
    [
      correspondentLookupByName,
      handleCorrespondentCreate,
      refreshCurrentFolder,
      resolveTargetDocumentIds,
      setStatusMessage,
    ],
  );

  const handleBulkCorrespondentRemove = useCallback(
    async ({ assignments = [], documentIds }) => {
      if (!assignments.length) {
        setStatusMessage('Select a correspondent to remove.', 'error');
        return;
      }

      const targets = resolveTargetDocumentIds(documentIds);

      if (!targets.length) {
        setStatusMessage('Select documents before removing correspondents.', 'error');
        return;
      }

      const normalizedAssignments = assignments.map((entry) => ({
        correspondent_id: entry.correspondent_id,
      }));

      const response = await api.post('/documents/bulk/correspondents', {
        document_ids: targets,
        assignments: normalizedAssignments,
        action: 'remove',
      });

      const { assigned = 0, removed = 0 } = response.data || {};
      await refreshCurrentFolder();

      if (removed > 0) {
        const removedSuffix = removed === 1 ? '' : 's';
        setStatusMessage(
          `Correspondent removed from ${removed} link${removedSuffix}.`,
          'success',
        );
      } else if (assigned > 0) {
        const assignedSuffix = assigned === 1 ? '' : 's';
        setStatusMessage(`Correspondent updated ${assigned} link${assignedSuffix}.`, 'info');
      } else {
        setStatusMessage('No correspondents changed.', 'info');
      }
    },
    [refreshCurrentFolder, resolveTargetDocumentIds, setStatusMessage],
  );

  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 bulkTagOperation = useCallback(
    async ({ labels, action, documentIds }) => {
      const normalized = labels.map((label) => label.trim()).filter((label) => label.length > 0);
      if (!normalized.length) {
        return { ok: false, reason: 'no-labels' };
      }
      const targetDocumentIds = resolveTargetDocumentIds(documentIds);
      if (!targetDocumentIds.length) {
        return { ok: false, reason: 'no-selection' };
      }

      let tagIds = [];

      if (action === 'remove') {
        const missing = normalized.find(
          (label) => !tags.some((tag) => tag.label.toLowerCase() === label.toLowerCase()),
        );
        if (missing) {
          return { ok: false, reason: 'tag-missing', label: missing };
        }

        tagIds = normalized.map((label) => {
          const tag = tags.find((item) => item.label.toLowerCase() === label.toLowerCase());
          return tag?.id;
        }).filter(Boolean);
      }

      setLoading(true);
      try {
        if (action === 'add') {
          const createdIds = [];
          for (const label of normalized) {
            let tag = tags.find((item) => item.label.toLowerCase() === label.toLowerCase()) || null;
            if (!tag) {
              const payload = tagManager.buildPayload({ label });
              const { data } = await api.post('/tags', payload);
              tag = data;
              await refreshTags();
            }
            createdIds.push(tag.id);
          }
          tagIds = Array.from(new Set(createdIds));
        }

        tagIds = Array.from(new Set(tagIds));

        if (!tagIds.length) {
          return { ok: false, reason: 'no-tags' };
        }

        await api.post('/documents/bulk/tags', {
          document_ids: targetDocumentIds,
          tag_ids: tagIds,
          action,
        });

        await refreshCurrentFolder();

        return {
          ok: true,
          tagCount: tagIds.length,
          docsCount: targetDocumentIds.length,
        };
      } catch (error) {
        const message =
          error.response?.data?.error ||
          (action === 'add' ? 'Failed to assign tags.' : 'Failed to remove tags.');
        notifyApiError(error, message);
        return { ok: false, reason: 'request-failed' };
      } finally {
        setLoading(false);
      }
    },
    [
      resolveTargetDocumentIds,
      tags,
      refreshTags,
      refreshCurrentFolder,
      notifyApiError,
      setLoading,
      tagManager,
    ],
  );

  const handleBulkTagAddFromDetail = useCallback(
    async ({ label, input, documentIds }) => {
      const trimmed = typeof label === 'string' ? label.trim() : '';
      if (!trimmed) {
        setStatusMessage('Enter a tag label.', 'error');
        return;
      }
      const targetIds = resolveTargetDocumentIds(documentIds);
      if (!targetIds.length) {
        setStatusMessage('Select documents before assigning tags.', 'error');
        return;
      }
      const result = await bulkTagOperation({
        labels: [trimmed],
        action: 'add',
        documentIds: targetIds,
      });
      if (result?.ok) {
        const { tagCount, docsCount } = result;
        setStatusMessage(
          `Assigned ${tagCount} tag${tagCount === 1 ? '' : 's'} to ${docsCount} document${
            docsCount === 1 ? '' : 's'
          }.`,
          'success',
        );
        if (input) {
          input.value = '';
        }
      }
    },
    [bulkTagOperation, resolveTargetDocumentIds, setStatusMessage],
  );

  const handleBulkTagRemoveFromDetail = useCallback(
    async ({ label, input, documentIds }) => {
      const trimmed = typeof label === 'string' ? label.trim() : '';
      if (!trimmed) {
        setStatusMessage('Enter a tag label to remove.', 'error');
        return;
      }
      const targetIds = resolveTargetDocumentIds(documentIds);
      if (!targetIds.length) {
        setStatusMessage('Select documents before removing tags.', 'error');
        return;
      }
      const result = await bulkTagOperation({
        labels: [trimmed],
        action: 'remove',
        documentIds: targetIds,
      });
      if (result?.ok) {
        const { docsCount } = result;
        setStatusMessage(
          `Removed tags from ${docsCount} document${docsCount === 1 ? '' : 's'}.`,
          'success',
        );
        if (input) {
          input.value = '';
        }
      } else if (result?.reason === 'tag-missing') {
        setStatusMessage(`Tag “${result.label}” not found.`, 'error');
      }
    },
    [bulkTagOperation, resolveTargetDocumentIds, setStatusMessage],
  );
  const handleBulkSelectionReanalyze = useCallback(
    async (documentIdsOverride = null) => {
      const targetIds = resolveTargetDocumentIds(documentIdsOverride);
      if (!targetIds.length) {
        setStatusMessage('Select documents before requesting re-analysis.', 'error');
        return;
      }

      setLoading(true);
      try {
        const { data } = await api.post('/documents/bulk/reanalyze', {
          document_ids: targetIds,
          force: true,
        });
        const queued = data?.queued ?? targetIds.length;
        setStatusMessage(
          `Queued re-analysis for ${queued} document${queued === 1 ? '' : 's'}.`,
          'success',
        );
      } catch (error) {
        const message =
          error.response?.data?.error || 'Failed to queue document re-analysis.';
        notifyApiError(error, message);
      } finally {
        setLoading(false);
      }
    },
    [resolveTargetDocumentIds, notifyApiError, setStatusMessage],
  );

  const uploadFile = useCallback(
    async (file, targetFolderId) => {
      if (!file || file.size === 0) {
        setStatusMessage('Skipped empty file.', 'error');
        return null;
      }

      const formData = new FormData();
      formData.append('file', file, file.name);
      if (targetFolderId && targetFolderId !== 'root') {
        formData.append('folder_id', targetFolderId);
      }

      try {
        const { data, status } = await api.post('/documents', formData);
        const duplicate = data?.reused || status === 200;
        setStatusMessage(
          duplicate
            ? `${file.name} already exists; reused existing document.`
            : `Uploaded ${file.name}`,
          duplicate ? 'info' : 'success',
        );
        return data;
      } catch (error) {
        const message = error.response?.data?.error || `Failed to upload ${file.name}.`;
        notifyApiError(error, message);
        throw error;
      }
    },
    [notifyApiError, setStatusMessage],
  );

  const folderPathCacheRef = useRef(new Map());

  const ensureFolderPathOnServer = useCallback(
    async (baseFolderId, segments) => {
      const trimmedSegments = segments.map((segment) => segment.trim()).filter(Boolean);
      if (trimmedSegments.length === 0) {
        return baseFolderId ?? null;
      }

      const cacheKey = `${baseFolderId ?? 'ROOT'}:${trimmedSegments.join('/')}`;
      const cache = folderPathCacheRef.current;
      if (cache.has(cacheKey)) {
        return cache.get(cacheKey);
      }

      const payload = {
        parent_id: baseFolderId && baseFolderId !== 'root' ? baseFolderId : null,
        segments: trimmedSegments,
      };

      const { data } = await api.post('/folders/path', payload);
      cache.set(cacheKey, data.folder.id);
      return data.folder.id;
    },
    [],
  );

  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 dragPreviewRef = useRef(null);

  const destroyDragPreview = useCallback(() => {
    const node = dragPreviewRef.current;
    if (node && node.parentNode) {
      node.parentNode.removeChild(node);
    }
    dragPreviewRef.current = null;
  }, []);

  useEffect(() => destroyDragPreview, [destroyDragPreview]);

  const createDragPreview = useCallback(
    ({ documents = [], folders = [] } = {}) => {
      destroyDragPreview();

      const docEntries = (documents || []).filter(Boolean);
      const folderEntries = (folders || []).filter(Boolean);
      const totalCount = docEntries.length + folderEntries.length;
      if (!totalCount) {
        return null;
      }

      const maxVisible = 4;
      const size = 64;
      const canvasSize = Math.round(size * 1.6);

      const visibleItems = [];
      docEntries.slice(0, maxVisible).forEach((doc) => {
        visibleItems.push({ type: 'document', payload: doc });
      });

      if (visibleItems.length < maxVisible) {
        folderEntries
          .slice(0, maxVisible - visibleItems.length)
          .forEach((folderId) => visibleItems.push({ type: 'folder', payload: folderId }));
      }

      const wrapper = document.createElement('div');
      wrapper.className = 'document-drag-preview';
      wrapper.style.setProperty('--drag-preview-size', `${canvasSize}px`);

      visibleItems.forEach((item, index) => {
        const layer = document.createElement('div');
        layer.className = 'document-drag-preview__thumb';
        const rotationMagnitude = Math.random() * 8 + 2; // 2..10 degrees
        const rotation = (index % 2 === 0 ? 1 : -1) * rotationMagnitude;
        layer.style.setProperty('--rotation-deg', `${rotation}deg`);

        if (item.type === 'document') {
          const doc = item.payload;
          const rowEl = doc?.id
            ? document.getElementById(`document-row-${doc.id}`) ||
              document.getElementById(`document-card-${doc.id}`)
            : null;
          const thumbnailEl = rowEl?.querySelector('.document-thumbnail');
          const placeholderEl = rowEl?.querySelector('.thumb-placeholder');
          const wrapperEl = rowEl?.querySelector('.document-thumbnail-wrapper');
          const aspectAttr = wrapperEl?.dataset?.thumbnailAspect;
          const aspectRatio = aspectAttr ? parseFloat(aspectAttr) : null;

          let thumbWidth = size;
          let thumbHeight = size;
          if (Number.isFinite(aspectRatio) && aspectRatio > 0) {
            if (aspectRatio >= 1) {
              thumbWidth = size;
              thumbHeight = Math.max(size / aspectRatio, size * 0.5);
            } else {
              thumbHeight = size;
              thumbWidth = Math.max(size * aspectRatio, size * 0.5);
            }
          }
          layer.style.width = `${Math.round(thumbWidth)}px`;
          layer.style.height = `${Math.round(thumbHeight)}px`;

          const thumbSrc = thumbnailEl?.currentSrc || thumbnailEl?.src || null;

          if (thumbSrc) {
            layer.classList.add('document-drag-preview__thumb--image');
            layer.style.backgroundImage = `url("${thumbSrc}")`;
          } else if (placeholderEl instanceof HTMLElement) {
            const content = placeholderEl.cloneNode(true);
            content.style.pointerEvents = 'none';
            layer.appendChild(content);
          } else {
            layer.textContent = 'DOC';
          }
        } else {
          const folderId = item.payload;
          const rowEl = folderId ? document.getElementById(`folder-row-${folderId}`) : null;
          const iconEl = rowEl?.querySelector('.thumb-icon');

          let content = null;
          if (iconEl instanceof HTMLElement) {
            content = iconEl.cloneNode(true);
            content.classList.add('document-drag-preview__folder-thumb');
            const svg = content.querySelector('svg');
            if (svg) {
              svg.setAttribute('width', '48');
              svg.setAttribute('height', '48');
            }
          }

          if (!content) {
            content = document.createElement('div');
            content.className = 'document-drag-preview__folder-placeholder';
            content.textContent = 'Folder';
          }

          layer.appendChild(content);
        }

        wrapper.appendChild(layer);
      });

      if (totalCount > 1) {
        const badge = document.createElement('div');
        badge.className = 'document-drag-preview__count';
        badge.textContent = `${totalCount}`;
        wrapper.appendChild(badge);
      }

      document.body.appendChild(wrapper);
      dragPreviewRef.current = wrapper;
      return wrapper;
    },
    [destroyDragPreview],
  );

  const handleDocumentDragStart = useCallback(
    (event, documentOrId) => {
      const documentId = typeof documentOrId === 'string' ? documentOrId : documentOrId?.id;
      if (!documentId) {
        return;
      }

      const documentKey = resolveDocumentRowKey(documentId);
      if (!documentKey) {
        return;
      }

      const isGridView = documentsViewMode === 'grid';
      const isAlreadySelected = selectedDocumentIds.includes(documentId);
      const selection = isAlreadySelected
        ? [...selectedDocumentIds]
        : isGridView
          ? [...selectedDocumentIds, documentId]
          : [documentId];
      const folderSelection = selectedFolderIds.length ? [...selectedFolderIds] : [];

      if (!isAlreadySelected && !isGridView) {
        applySelection([documentKey], {
          anchor: documentKey,
          interactedKeys: [documentKey],
        });
      }

      const previewDocs = selection.map((id) => documentLookup.get(id) || null).filter(Boolean);
      const previewNode = createDragPreview({
        documents: previewDocs,
        folders: folderSelection,
      });

      setDraggedDocumentIds(selection);
      if (folderSelection.length) {
        setDraggedFolderId(folderSelection[0] || null);
      }
      event.dataTransfer.effectAllowed = 'move';
      try {
        event.dataTransfer.setData(
          'application/x-papercrate-doc-list',
          JSON.stringify(selection),
        );
        if (folderSelection.length) {
          event.dataTransfer.setData(
            'application/x-papercrate-folder-list',
            JSON.stringify(folderSelection),
          );
          if (folderSelection.length === 1) {
            event.dataTransfer.setData('application/x-papercrate-folder', folderSelection[0]);
          }
        }
      } catch (error) {
        console.warn('[documents] Failed to populate drag payload', error);
      }
      if (previewNode) {
        const width = previewNode.offsetWidth || 96;
        const height = previewNode.offsetHeight || 96;
        event.dataTransfer.setDragImage(previewNode, width / 2, height / 2);
      }
      event.currentTarget.classList.add('dragging');
    },
    [
      selectedDocumentIds,
      selectedFolderIds,
      applySelection,
      documentLookup,
      createDragPreview,
      setDraggedFolderId,
      documentsViewMode,
    ],
  );

  const handleDocumentDragEnd = useCallback(
    (event) => {
      setDraggedDocumentIds([]);
      event.currentTarget.classList.remove('dragging');
      destroyDragPreview();
      setDraggedFolderId(null);
    },
    [destroyDragPreview, setDraggedFolderId],
  );

  const handleFolderDragStart = useCallback(
    (event, folderId) => {
      if (folderId === 'root') {
        return;
      }
      event.stopPropagation();
      const folderKey = resolveFolderRowKey(folderId);
      const isAlreadySelected = folderKey ? selectedEntries.includes(folderKey) : false;

      let effectiveFolderSelection = selectedFolderIds;
      let effectiveDocumentSelection = selectedDocumentIds;

      if (!isAlreadySelected && folderKey) {
        effectiveFolderSelection = [folderId];
        effectiveDocumentSelection = [];
        handleEntrySelection(folderKey, { preventDefault: () => {} });
      }

      const uniqueFolders = effectiveFolderSelection.length
        ? Array.from(new Set(effectiveFolderSelection.filter(Boolean)))
        : [folderId];

      setDraggedFolderId(folderId);
      if (effectiveDocumentSelection.length) {
        setDraggedDocumentIds(effectiveDocumentSelection);
      }

      event.dataTransfer.effectAllowed = 'move';
      try {
        event.dataTransfer.setData(
          'application/x-papercrate-folder-list',
          JSON.stringify(uniqueFolders),
        );
        if (uniqueFolders.length === 1) {
          event.dataTransfer.setData('application/x-papercrate-folder', uniqueFolders[0]);
        }
        if (effectiveDocumentSelection.length) {
          event.dataTransfer.setData(
            'application/x-papercrate-doc-list',
            JSON.stringify(effectiveDocumentSelection),
          );
        }
      } catch (error) {
        console.warn('[folders] Failed to set drag payload', error);
      }

      const previewDocs = effectiveDocumentSelection
        .map((id) => documentLookup.get(id) || null)
        .filter(Boolean);
      const previewNode = createDragPreview({
        documents: previewDocs,
        folders: uniqueFolders,
      });
      if (previewNode) {
        const width = previewNode.offsetWidth || 96;
        const height = previewNode.offsetHeight || 96;
        event.dataTransfer.setDragImage(previewNode, width / 2, height / 2);
      }
      event.currentTarget?.classList.add('dragging');
    },
    [
      selectedEntries,
      selectedFolderIds,
      selectedDocumentIds,
      setDraggedFolderId,
      setDraggedDocumentIds,
      handleEntrySelection,
      documentLookup,
      createDragPreview,
    ],
  );

  const handleFolderDragEnd = useCallback(
    (event) => {
      if (event?.currentTarget) {
        event.currentTarget.classList.remove('dragging');
      }
      setDraggedFolderId(null);
      setDraggedDocumentIds([]);
      destroyDragPreview();
    },
    [setDraggedFolderId, setDraggedDocumentIds, destroyDragPreview],
  );

  const ensurePreviewUrl = useCallback(
    async (documentId, { force = false } = {}) => {
      if (!documentId) return null;

      const existing = previewEntries.get(documentId) || null;
      const now = Date.now();
      const expiresAt = typeof existing?.expiresAt === 'number' ? existing.expiresAt : null;
      if (!force && existing && (!expiresAt || expiresAt > now)) {
        return existing;
      }

      if (!force && previewInflightRef.current.has(documentId)) {
        return previewInflightRef.current.get(documentId);
      }

      const request = (async () => {
        try {
          const docResponse = await api.get(`/documents/${documentId}`);
          const downloadPath = docResponse.data?.document?.current_version?.download_path;
          if (!downloadPath || !resolveApiPath) {
            throw new Error('Document missing download path');
          }

          const href = resolveApiPath(downloadPath);
          const entry = {
            url: href,
            contentType: docResponse.data?.document?.current_version?.version?.content_type || null,
            filename: docResponse.data?.document?.filename,
            expiresAt: Date.now() + 5 * 60 * 1000,
          };
          setPreviewEntries((prev) => {
            const next = new Map(prev);
            next.set(documentId, entry);
            return next;
          });
          return entry;
        } catch (error) {
          notifyApiError(error, 'Unable to fetch document preview.');
          throw error;
        } finally {
          previewInflightRef.current.delete(documentId);
        }
      })();

      previewInflightRef.current.set(documentId, request);
      return request;
    },
    [previewEntries, notifyApiError],
  );

  const extractFilesFromDataTransfer = useCallback(async (dataTransfer) => {
    if (!dataTransfer) {
      throw new Error('No drop payload found.');
    }

    const items = Array.from(dataTransfer.items || []);
    console.info('[Uploads] drop start', { items: items.length, files: (dataTransfer.files || []).length });

    const results = [];
    const seenKeys = new Set();

    const pushFile = (file, ancestors = []) => {
      if (!file) return;
      const segments = (ancestors || []).filter(Boolean);
      const key = `${segments.join('/')}/${file.name}:${file.size}`;
      if (seenKeys.has(key)) {
        // skipped duplicate
        return;
      }
      seenKeys.add(key);
      results.push({ file, segments });
      // queued file
    };

    const readAllEntries = async (reader) => {
      const entries = [];
      let batch = [];
      do {
        // eslint-disable-next-line no-await-in-loop
        batch = await new Promise((resolve, reject) => reader.readEntries(resolve, reject));
        if (batch.length) {
          entries.push(...batch);
        }
      } while (batch.length);
      return entries;
    };

    const walkEntry = async (entry, ancestors = []) => {
      if (!entry) return;
      if (entry.isFile) {
        const file = await new Promise((resolve, reject) => {
          try {
            entry.file(resolve, reject);
          } catch (error) {
            console.warn('[Uploads] entry.file failed', error);
            reject(error);
          }
        });
        pushFile(file, ancestors);
        return;
      }
      if (entry.isDirectory) {
        const nextAncestors = entry.name ? [...ancestors, entry.name] : [...ancestors];
        const reader = entry.createReader();
        const entries = await readAllEntries(reader);
        for (const child of entries) {
          // eslint-disable-next-line no-await-in-loop
          await walkEntry(child, nextAncestors);
        }
      }
    };

    await Promise.all(
      items.map(async (item, index) => {
        if (item.kind !== 'file') return;

        const fileFromItem = typeof item.getAsFile === 'function' ? item.getAsFile() : null;
        if (fileFromItem) {
          const relativePath =
            typeof fileFromItem.webkitRelativePath === 'string' ? fileFromItem.webkitRelativePath : '';
          const segments = relativePath
            ? relativePath
                .split('/')
                .slice(0, -1)
                .filter(Boolean)
            : [];
          pushFile(fileFromItem, segments);
        }

        if (typeof item.webkitGetAsEntry === 'function') {
          try {
            const entry = item.webkitGetAsEntry();
            if (entry) {
              // processing entry
              await walkEntry(entry, []);
              return;
            }
          } catch (error) {
            console.warn('[Uploads] webkitGetAsEntry failed', error);
          }
        }

        if (!fileFromItem) {
          console.info('[Uploads] item missing file handle', index);
        }
      }),
    );

    Array.from(dataTransfer.files || []).forEach((file) => {
      if (!file) return;
      // FileList entry suppressed
      const relativePath =
        typeof file.webkitRelativePath === 'string' ? file.webkitRelativePath : '';
      const segments = relativePath
        ? relativePath
            .split('/')
            .slice(0, -1)
            .filter(Boolean)
        : [];
      pushFile(file, segments);
    });

    if (!results.length) {
      throw new Error('No files detected in drop payload.');
    }

    console.info('[Uploads] prepared files', results.length);

    return results;
  }, []);


  const handleFileDrop = useCallback(
    async (dataTransfer, targetFolderId) => {
      if (!token) {
        setStatusMessage('Please log in before uploading.', 'error');
        return;
      }

      setLoading(true);

      try {
        folderPathCacheRef.current.clear();

        let extracted;
        try {
          extracted = await extractFilesFromDataTransfer(dataTransfer);
        } catch (error) {
          const message = error.message || 'Failed to process dropped files.';
          notifyApiError(error, message);
          return;
        }

        if (!extracted.length) {
          setStatusMessage('No files to upload.', 'info');
          return;
        }
        const baseFolderId =
          targetFolderId && targetFolderId !== 'root' ? targetFolderId : null;

        for (const { file, segments } of extracted) {
          // eslint-disable-next-line no-await-in-loop
          const destinationId = segments.length
            ? await ensureFolderPathOnServer(baseFolderId, segments)
            : baseFolderId;

          const uploadTarget =
            destinationId ??
            (targetFolderId && targetFolderId !== 'root' ? targetFolderId : 'root');

          // eslint-disable-next-line no-await-in-loop
          await uploadFile(file, uploadTarget);
        }

        await refreshCurrentFolder();

        if (
          targetFolderId &&
          targetFolderId !== 'root' &&
          targetFolderId !== selectedFolder
        ) {
          await ensureFolderData(targetFolderId, { force: true, prefetchDepth: 1 });
        }
      } catch (error) {
        notifyApiError(error, 'Failed to upload files.');
      } finally {
        setLoading(false);
      }
    },
    [
      token,
      extractFilesFromDataTransfer,
      ensureFolderPathOnServer,
      uploadFile,
      refreshCurrentFolder,
      selectedFolder,
      ensureFolderData,
      notifyApiError,
      setStatusMessage,
    ],
  );

  const normalizeDocumentId = (value) => {
    if (!value) return null;
    if (typeof value === 'object' && value.id) {
      return value.id;
    }
    return value;
  };

  const moveDocumentsToFolder = useCallback(
    async (documentIds, targetFolderId) => {
      const uniqueIds = Array.from(
        new Set((documentIds || []).map((value) => normalizeDocumentId(value)).filter(Boolean)),
      );
      if (!uniqueIds.length) return;

      const uniqueIdSet = new Set(uniqueIds);
      const target = targetFolderId === 'root' ? null : targetFolderId;
      const targetLabel =
        target === null
          ? DEFAULT_FOLDER_NAME
          : folderLabelMap.get(targetFolderId) || 'target folder';

      const movedDocs = uniqueIds
        .map((id) => {
          const doc = documentLookup.get(id);
          if (!doc) {
            return null;
          }
          return {
            id,
            sourceFolderId: doc.folder_id ?? null,
            document: doc,
          };
        })
        .filter(Boolean);

      const updatedDocsMap = new Map();
      const resolveTargetName = () => {
        if (!targetLabel) {
          return null;
        }
        const segments = String(targetLabel).split('/');
        return segments[segments.length - 1] || targetLabel;
      };
      const targetName = resolveTargetName();

      movedDocs.forEach(({ id, document }) => {
        if (!document) {
          return;
        }
        const updated = {
          ...document,
          folder_id: target,
        };
        if (targetLabel) {
          updated.folder_path = targetLabel;
          if (targetName) {
            updated.folder_name = targetName;
          }
        } else if (target === null) {
          updated.folder_path = DEFAULT_FOLDER_NAME;
          updated.folder_name = DEFAULT_FOLDER_NAME;
        }
        updatedDocsMap.set(id, updated);
      });

      const pruneRowCollection = (collection) =>
        collection.filter((key) => {
          if (!isDocumentRowKey(key)) {
            return true;
          }
          const id = getRowId(key);
          return id ? !uniqueIdSet.has(id) : true;
        });

      setLoading(true);
      try {
        if (uniqueIds.length === 1) {
          await api.patch(`/documents/${uniqueIds[0]}/folder`, { folder_id: target });
        } else {
          await api.post('/documents/bulk/move', {
            document_ids: uniqueIds,
            folder_id: target,
          });
        }

        const count = uniqueIds.length;
        const suffix = count === 1 ? '' : 's';
        setStatusMessage(`Moved ${count} document${suffix} to ${targetLabel}.`, 'success');

        if (updatedDocsMap.size) {
          mapDocumentCaches((doc) => {
            if (!doc || !uniqueIdSet.has(doc.id)) {
              return doc;
            }
            const updated = updatedDocsMap.get(doc.id);
            if (updated) {
              return updated;
            }
            return { ...doc, folder_id: target };
          });
        } else {
          mapDocumentCaches((doc) => {
            if (!doc || !uniqueIdSet.has(doc.id)) {
              return doc;
            }
            return { ...doc, folder_id: target };
          });
        }

        if (uniqueIdSet.size) {
          setDocuments((prev) => prev.filter((doc) => !uniqueIdSet.has(doc.id)));
          setFolderContents((prev) => {
            if (!prev.size) {
              return prev;
            }
            let changed = false;
            const next = new Map(prev);
            movedDocs.forEach(({ id, sourceFolderId }) => {
              const sourceKey = sourceFolderId || 'root';
              const entry = next.get(sourceKey);
              if (!entry?.documents?.length) {
                return;
              }
              const filteredDocs = entry.documents.filter((doc) => doc.id !== id);
              if (filteredDocs.length !== entry.documents.length) {
                changed = true;
                next.set(sourceKey, { ...entry, documents: filteredDocs });
              }
            });
            return changed ? next : prev;
          });

          setSelectedEntries((prev) => pruneRowCollection(prev));
          setSelectionOrder((prev) => pruneRowCollection(prev));
          selectionOrderRef.current = pruneRowCollection(selectionOrderRef.current);
          if (
            selectionAnchorRef.current &&
            isDocumentRowKey(selectionAnchorRef.current) &&
            uniqueIdSet.has(getRowId(selectionAnchorRef.current))
          ) {
            selectionAnchorRef.current = null;
          }
          if (focusedDocumentId && uniqueIdSet.has(focusedDocumentId)) {
            setFocusedDocumentId(null);
          }
          if (
            focusedRowKey &&
            isDocumentRowKey(focusedRowKey) &&
            uniqueIdSet.has(getRowId(focusedRowKey))
          ) {
            setFocusedRowKey(null);
          }
        }

        if (targetFolderId && targetFolderId !== selectedFolder) {
          await ensureFolderData(targetFolderId, { force: true, prefetchDepth: 1 });
        }
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to move documents.';
        notifyApiError(error, message);
      } finally {
        setLoading(false);
      }
    },
    [
      documentLookup,
      ensureFolderData,
      folderLabelMap,
      focusedDocumentId,
      mapDocumentCaches,
      notifyApiError,
      selectedFolder,
      setDocuments,
      setFolderContents,
      setFocusedDocumentId,
      focusedRowKey,
      setFocusedRowKey,
      setSelectionOrder,
      setSelectedEntries,
      setStatusMessage,
      selectionAnchorRef,
      selectionOrderRef,
    ],
  );

  const handleThumbnailRegeneration = useCallback(
    async (documentId) => {
      if (!token) {
        setStatusMessage('Log in to manage assets.', 'error');
        return;
      }
      setLoading(true);
      try {
        await api.post(`/documents/${documentId}/assets`, null, {
          params: { force: true },
        });
        setStatusMessage('Document re-analysis queued.', 'info');
        await refreshCurrentFolder();
      } catch (error) {
        const message =
          error.response?.data?.error || 'Failed to request thumbnail generation.';
        notifyApiError(error, message);
      } finally {
        setLoading(false);
      }
    },
    [token, refreshCurrentFolder, notifyApiError, setStatusMessage],
  );

  const ensurePreviewData = useCallback(
    async (documentId) => {
      if (!documentId) return null;

      const findInCache = () => {
        const pool = searchResults ?? documents;
        return pool.find((item) => item.id === documentId) || null;
      };

      let doc = findInCache();

      if (!doc) {
        const { data } = await api.get(`/documents/${documentId}`);
        const hydratedDetail = assetManager.hydrateDetail(data);
        const fetched = hydratedDetail?.document || data.document || data;
        doc = fetched ? assetManager.hydrateDocument(fetched) : null;
        if (!doc) {
          throw new Error('Document metadata unavailable.');
        }

        setDocuments((prev) => {
          if (prev.some((item) => item.id === doc.id)) {
            return prev;
          }
          return [doc, ...prev];
        });
      }

      if (!previewReturnPathRef.current) {
        const fallbackFolderId = doc?.folder_id || 'root';
        previewReturnPathRef.current =
          fallbackFolderId === 'root' ? '/documents' : `/documents/folder/${fallbackFolderId}`;
      }

      await ensurePreviewUrl(documentId, { force: false });
      setActivePreviewId(documentId);
      return doc;
    },
    [
      searchResults,
      documents,
      assetManager,
      setDocuments,
      ensurePreviewUrl,
      setActivePreviewId,
    ],
  );

  const openDocumentPreview = useCallback(
    (documentId, { replace = false } = {}) => {
      if (!documentId) return;
      detailPanelControlRef.current.close();
      previewReturnPathRef.current = `${location.pathname}${location.search}`;
      navigate(`/documents/${documentId}`, { replace });
    },
    [navigate, location.pathname, location.search],
  );

  const closeDocumentPreview = useCallback(
    (folderId = null) => {
      const fallbackPath = previewReturnPathRef.current;
      previewReturnPathRef.current = null;

      if (fallbackPath) {
        navigate(fallbackPath, { replace: false });
        return;
      }

      const targetId = folderId || selectedFolder || 'root';
      const path = targetId === 'root' ? '/documents' : `/documents/folder/${targetId}`;
      navigate(path, { replace: false });
    },
    [navigate, selectedFolder],
  );

  const handleDocumentsDelete = useCallback(
    async (documentIds, { showMessage = true, manageLoading = true } = {}) => {
      if (!documentIds || documentIds.length === 0) {
        return false;
      }

      if (!token) {
        setStatusMessage('Log in to manage documents.', 'error');
        return false;
      }

      if (manageLoading) {
        setLoading(true);
      }

      try {
        await Promise.all(
          documentIds.map((documentId) => api.delete(`/documents/${documentId}`)),
        );

        removeDocumentsFromCaches(documentIds);

        if (documentIds.includes(previewDocumentId)) {
          closeDocumentPreview();
        }

        if (showMessage) {
          const message = documentIds.length === 1 ? 'Document deleted.' : 'Documents deleted.';
          setStatusMessage(message, 'success');
        }

        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to delete document.';
        notifyApiError(error, message);
        return false;
      } finally {
        if (manageLoading) {
          setLoading(false);
        }
      }
    },
    [
      token,
      previewDocumentId,
      closeDocumentPreview,
      removeDocumentsFromCaches,
      notifyApiError,
      setStatusMessage,
      setLoading,
    ],
  );

  useEffect(() => {
    if (!previewDocumentId) return;
    const handleKeyDown = (event) => {
      if (event.key === 'Escape') {
        closeDocumentPreview();
      }
    };
    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [previewDocumentId, closeDocumentPreview]);

  useEffect(() => {
    if (!previewDocumentId) {
      return;
    }

    let cancelled = false;

    ensurePreviewData(previewDocumentId).catch((error) => {
      if (cancelled) {
        return;
      }
      notifyApiError(error, 'Failed to open document preview.');
      closeDocumentPreview();
    });

    return () => {
      cancelled = true;
    };
  }, [previewDocumentId, ensurePreviewData, notifyApiError, closeDocumentPreview]);

  const handleDocumentTitleUpdate = useCallback(
    async (documentId, nextTitle) => {
      const trimmed = nextTitle.trim();
      if (!trimmed) {
        setStatusMessage('Document title cannot be empty.', 'error');
        return false;
      }

      setLoading(true);
      try {
      const { data } = await api.patch(`/documents/${documentId}`, { title: trimmed });
      const updatedDocument = extractDocumentFromResponse(data);

      updateDocumentCaches(documentId, (doc) => {
        if (updatedDocument) {
          return { ...doc, ...updatedDocument };
        }
        return { ...doc, title: trimmed };
      });

        setStatusMessage('Document title updated.', 'success');
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to update document title.';
        notifyApiError(error, message);
        return false;
      } finally {
        setLoading(false);
      }
    },
    [notifyApiError, setStatusMessage, updateDocumentCaches, extractDocumentFromResponse],
  );

  const handleDocumentIssuedUpdate = useCallback(
    async (documentId, nextIssuedDate) => {
      setLoading(true);
      const payload = { issued_at: nextIssuedDate || null };
      try {
        const { data } = await api.patch(`/documents/${documentId}`, payload);
        const updatedDocument = extractDocumentFromResponse(data);

        updateDocumentCaches(documentId, (doc) => {
          if (updatedDocument) {
            return { ...doc, ...updatedDocument };
          }
          return { ...doc, issued_at: payload.issued_at };
        });

        const message = payload.issued_at ? 'Issued date updated.' : 'Issued date cleared.';
        setStatusMessage(message, 'success');
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to update issued date.';
        notifyApiError(error, message);
        return false;
      } finally {
        setLoading(false);
      }
    },
    [extractDocumentFromResponse, notifyApiError, setStatusMessage, updateDocumentCaches],
  );

  const applyTagRemovalToCaches = useCallback(
    (documentId, tagId) => {
      if (!documentId || !tagId) {
        return;
      }

      updateDocumentCaches(documentId, (doc) => {
        if (!Array.isArray(doc.tags)) {
          return doc;
        }
        const nextTags = doc.tags.filter((tag) => tag.id !== tagId);
        if (nextTags.length === doc.tags.length) {
          return doc;
        }
        return { ...doc, tags: nextTags };
      });
    },
    [updateDocumentCaches],
  );

  const handleTagRemove = useCallback(
    async (documentId, tagId, { refreshTagList = true, showMessage = true } = {}) => {
      if (!documentId || !tagId) {
        return false;
      }

      try {
        await api.delete(`/documents/${documentId}/tags/${tagId}`);
        applyTagRemovalToCaches(documentId, tagId);
        if (refreshTagList) {
          await refreshTags();
        }
        if (showMessage) {
          setStatusMessage('Tag removed.', 'success');
        }
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to remove tag.';
        notifyApiError(error, message);
        return false;
      }
    },
    [refreshTags, notifyApiError, setStatusMessage, applyTagRemovalToCaches],
  );

  const handleTagAdd = useCallback(
    async (document, label, extras = null) => {
      const normalizedLabel = tagManager.normalizeLabel(label);
      const optionCandidate =
        extras && typeof extras === 'object' && 'option' in extras ? extras.option : null;
      const input =
        extras && typeof extras === 'object' && 'input' in extras ? extras.input : null;

      let tag = null;
      if (optionCandidate && optionCandidate.id) {
        tag = tags.find((item) => item.id === optionCandidate.id) || optionCandidate;
      }
      if (!tag) {
        tag =
          tags.find((item) => item.label.toLowerCase() === normalizedLabel.toLowerCase()) || null;
      }
      try {
        if (!tag) {
          const payload = tagManager.buildPayload({ label: normalizedLabel });
          const { data } = await api.post('/tags', payload);
          tag = data;
          await refreshTags();
        }
        await api.post(`/documents/${document.id}/tags`, { tag_ids: [tag.id] });
        setStatusMessage('Tag assigned.', 'success');
        if (input && typeof input === 'object') {
          input.value = '';
        }
        await refreshCurrentFolder();
      } catch (error) {
        notifyApiError(error, 'Failed to assign tag.');
      }
    },
    [tags, refreshTags, refreshCurrentFolder, notifyApiError, setStatusMessage, tagManager],
  );

  const handleDocumentTagAttach = useCallback(
    async ({ documentId, tagId, tag: tagData = null }) => {
      if (!documentId || !tagId) {
        return false;
      }

      const resolveTagForCache = () => {
        const lookupTag = tagLookupById.get(tagId);
        const source = lookupTag ?? tagData;
        if (!source || source.id == null || typeof source.label !== 'string') {
          return null;
        }
        return {
          id: source.id,
          label: source.label,
          color: Object.prototype.hasOwnProperty.call(source, 'color') ? source.color : null,
        };
      };

      try {
        await api.post(`/documents/${documentId}/tags`, { tag_ids: [tagId] });
        updateDocumentCaches(documentId, (doc) => {
          if (!doc) {
            return doc;
          }
          const currentTags = Array.isArray(doc.tags) ? doc.tags : [];
          if (currentTags.some((existing) => existing?.id === tagId)) {
            return doc;
          }
          const resolvedTag = resolveTagForCache();
          if (!resolvedTag) {
            return doc;
          }
          return { ...doc, tags: [...currentTags, resolvedTag] };
        });
        setStatusMessage('Tag assigned.', 'success');
        if (documentsViewMode !== 'desk') {
          await refreshCurrentFolder();
        }
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to assign tag.';
        notifyApiError(error, message);
        return false;
      }
    },
    [
      refreshCurrentFolder,
      documentsViewMode,
      notifyApiError,
      setStatusMessage,
      updateDocumentCaches,
      tagLookupById,
    ],
  );


  const handleFolderDelete = useCallback(
    async (folderId, { showMessage = true, manageLoading = true } = {}) => {
      if (!token) {
        if (showMessage) {
          setStatusMessage('Log in to manage folders.', 'error');
        }
        return false;
      }
      if (!folderId || folderId === 'root') {
        if (showMessage) {
          setStatusMessage('The root folder cannot be removed.', 'error');
        }
        return false;
      }

      if (manageLoading) {
        setLoading(true);
      }

      try {
        const contents = await ensureFolderData(folderId, {
          force: true,
          prefetchDepth: 1,
        });
        const hasChildren = (contents.subfolders || []).length > 0;
        const hasDocs = (contents.documents || []).length > 0;
        if (hasChildren || hasDocs) {
          if (showMessage) {
            setStatusMessage('Folder must be empty before it can be deleted.', 'error');
          }
          return false;
        }

        await api.delete(`/folders/${folderId}`);

        setFolderNodes((prev) => {
          const next = new Map(prev);
          const node = next.get(folderId);
          next.delete(folderId);
          if (node) {
            const parentId = node.parentId || 'root';
            const parentNode = next.get(parentId);
            if (parentNode) {
              const remaining = parentNode.children.filter((id) => id !== folderId);
              next.set(parentId, {
                ...parentNode,
                children: remaining,
                hasChildren: remaining.length > 0,
              });
            }
          }
          return next;
        });

        setFolderContents((prev) => {
          const next = new Map(prev);
          next.delete(folderId);
          return next;
        });

        if (selectedFolder === folderId) {
          const node = folderNodes.get(folderId);
          const parentId = node?.parentId || 'root';
          setSelectedFolder(parentId);
          const parentContents = await ensureFolderData(parentId, {
            force: true,
            prefetchDepth: 1,
          });
          applySelectedFolder(parentId, parentContents);
        } else if (selectedFolder !== 'root') {
          await ensureFolderData(selectedFolder, { force: true, prefetchDepth: 1 });
        }

        if (showMessage) {
          setStatusMessage('Folder deleted.', 'success');
        }
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to delete folder.';
        notifyApiError(error, message);
        if (showMessage) {
          setStatusMessage(message, 'error');
        }
        return false;
      } finally {
        if (manageLoading) {
          setLoading(false);
        }
      }
    },
    [
      token,
      ensureFolderData,
      selectedFolder,
      folderNodes,
      applySelectedFolder,
      notifyApiError,
      setStatusMessage,
      setLoading,
    ],
  );

  const handleDeleteSelection = useCallback(async () => {
    const docIds = Array.isArray(selectedDocumentIds) ? selectedDocumentIds : [];
    const folderIds = Array.isArray(selectedFolderIds) ? selectedFolderIds : [];

    if (docIds.length === 0 && folderIds.length === 0) {
      return;
    }

    const parts = [];
    if (docIds.length) {
      parts.push(`${docIds.length} document${docIds.length === 1 ? '' : 's'}`);
    }
    if (folderIds.length) {
      parts.push(`${folderIds.length} folder${folderIds.length === 1 ? '' : 's'}`);
    }
    const descriptor = parts.join(' and ');
    const confirmation = parts.length === 1
      ? `Delete ${descriptor}? Folders must be empty before deletion. You can restore documents later from trash.`
      : `Delete ${descriptor}? Folders must be empty before deletion. You can restore documents later from trash.`;

    if (!window.confirm(confirmation)) {
      return;
    }

    setLoading(true);
    let docsOk = true;
    let foldersOk = true;

    try {
      if (docIds.length) {
        docsOk = await handleDocumentsDelete(docIds, { showMessage: false, manageLoading: false });
      }

      if (folderIds.length) {
        for (const folderId of folderIds) {
          const success = await handleFolderDelete(folderId, { showMessage: false, manageLoading: false });
          if (!success) {
            foldersOk = false;
          }
        }
      }
    } finally {
      setLoading(false);
    }

    if (!docsOk || !foldersOk) {
      setStatusMessage('Some items could not be deleted. Ensure folders are empty before deletion.', 'error');
      return;
    }

    clearDocumentSelection();

    const successParts = [];
    if (docIds.length) {
      successParts.push(docIds.length === 1 ? 'Document deleted.' : 'Documents deleted.');
    }
    if (folderIds.length) {
      successParts.push(folderIds.length === 1 ? 'Folder deleted.' : 'Folders deleted.');
    }

    setStatusMessage(successParts.join(' '), 'success');
  }, [
    selectedDocumentIds,
    selectedFolderIds,
    handleDocumentsDelete,
    handleFolderDelete,
    clearDocumentSelection,
    setLoading,
    setStatusMessage,
  ]);

  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 handleFolderRename = useCallback(
    async (folderId, nextName) => {
      if (!token) {
        setStatusMessage('Log in to rename folders.', 'error');
        return false;
      }
      if (!folderId || folderId === 'root') {
        setStatusMessage('The root folder cannot be renamed.', 'error');
        return false;
      }
      const trimmed = typeof nextName === 'string' ? nextName.trim() : '';
      if (!trimmed) {
        setStatusMessage('Folder name cannot be empty.', 'error');
        return false;
      }

      setLoading(true);
      try {
        await api.patch(`/folders/${folderId}`, { name: trimmed });

        setFolderNodes((prev) => {
          const next = new Map(prev);
          const node = next.get(folderId);
          if (node) {
            next.set(folderId, { ...node, name: trimmed });
          }
          return next;
        });

        setFolderContents((prev) => {
          if (!prev.has(folderId)) {
            return prev;
          }
          const next = new Map(prev);
          const existing = next.get(folderId) || {};
          const folderInfo = existing.folder
            ? { ...existing.folder, name: trimmed }
            : { id: folderId, name: trimmed };
          next.set(folderId, { ...existing, folder: folderInfo });
          return next;
        });

        setCurrentFolder((prev) => (prev?.id === folderId ? { ...prev, name: trimmed } : prev));

        setStatusMessage('Folder renamed.', 'success');
        return true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to rename folder.';
        notifyApiError(error, message);
        return false;
      } finally {
        setLoading(false);
      }
    },
    [token, notifyApiError, setStatusMessage],
  );

  const handleFolderCreate = useCallback(
    async (name) => {
      if (!token) {
        setStatusMessage('Log in to create folders.', 'error');
        return false;
      }
      if (!name.trim()) {
        setStatusMessage('Folder name cannot be empty.', 'error');
        return false;
      }
      const payload = {
        name: name.trim(),
        parent_id: selectedFolder === 'root' ? null : selectedFolder,
      };
      setLoading(true);
      let succeeded = false;
      try {
        const { data } = await api.post('/folders', payload);
        setStatusMessage('Folder created.', 'success');
        setFolderNodes((prev) => {
          const next = new Map(prev);
          const parentId = payload.parent_id || 'root';
          const parentNode = next.get(parentId);
          if (parentNode) {
            next.set(parentId, {
              ...parentNode,
              children: parentNode.children.concat([data.folder.id]),
              loaded: true,
              hasChildren: true,
            });
          }
          next.set(data.folder.id, {
            id: data.folder.id,
            name: data.folder.name,
            parentId: parentId,
            children: [],
            expanded: false,
            loaded: false,
            hasChildren: false,
          });
          return next;
        });
        await ensureFolderData(selectedFolder, { force: true, prefetchDepth: 1 });
        await selectFolder(data.folder.id, { immediate: true });
        succeeded = true;
      } catch (error) {
        const message = error.response?.data?.error || 'Failed to create folder.';
        notifyApiError(error, message);
        succeeded = false;
      } finally {
        setLoading(false);
      }
      return succeeded;
    },
    [token, selectedFolder, ensureFolderData, notifyApiError, setStatusMessage, selectFolder],
  );

  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 openSettings = useCallback(() => {
    navigate('/settings');
  }, [navigate]);

  useEffect(() => {
    if (!token) return undefined;

    if (!isFilterActive) {
      setSearchResults(null);
      setSearchLoading(false);
      return undefined;
    }

    let cancelled = false;
    let started = false;
    setSearchLoading(true);

    const debounce = setTimeout(async () => {
      started = true;
      setLoading(true);
      try {
        const params = {};
        const trimmedQuery = searchQuery.trim();
        if (trimmedQuery.length) {
          params.query = trimmedQuery;
        }
        if (activeTagFilters.length) {
          params.tags = activeTagFilters.join(',');
        }
        if (activeCorrespondentFilters.length) {
          params.correspondents = activeCorrespondentFilters.join(',');
        }
        const folderIdentifier = selectedFolder === 'root' ? null : selectedFolder;
        if (folderIdentifier) {
          params.folder_id = folderIdentifier;
        }
        const { data } = await api.get('/documents', { params });
        if (cancelled) return;

        const results = assetManager.hydrateDocuments(data || []);
        setSearchResults(results);

        if (!results.length) {
          setSearchLoading(false);
          setSelectedEntries([]);
          setFocusedDocumentId(null);
          selectionOrderRef.current = [];
          setSelectionOrder([]);
          selectionAnchorRef.current = null;
          return;
        }

        const resultKeys = results
          .map((doc) => resolveDocumentRowKey(doc.id))
          .filter(Boolean);

        let targetKey = null;
        let nextSelectionKeys = [];

        setSelectedEntries((previous) => {
          const previousDocKeys = previous.filter(isDocumentRowKey);
          const filtered = previousDocKeys.filter((key) => resultKeys.includes(key));
          if (filtered.length) {
            targetKey = filtered[filtered.length - 1];
            nextSelectionKeys = filtered;
            return filtered;
          }
          targetKey = null;
          nextSelectionKeys = [];
          return [];
        });

        selectionOrderRef.current = nextSelectionKeys;
        setSelectionOrder(nextSelectionKeys);

        setFocusedDocumentId((previous) => {
          if (previous && resultKeys.includes(resolveDocumentRowKey(previous))) {
            return previous;
          }
          return null;
        });

        selectionAnchorRef.current = targetKey;

        // rely on hydrated search results; assets refresh on demand
      } catch (error) {
        if (cancelled) return;
        notifyApiError(error, 'Search failed. Please try again.');
        setSearchResults(null);
      } finally {
        if (!cancelled && started) {
          setLoading(false);
          setSearchLoading(false);
        }
      }
    }, 300);

    return () => {
      cancelled = true;
      clearTimeout(debounce);
      if (started) {
        setLoading(false);
        setSearchLoading(false);
      }
    };
  }, [
    token,
    isFilterActive,
    searchQuery,
    activeTagFilters,
    activeCorrespondentFilters,
    selectedFolder,
    notifyApiError,
    assetManager,
    selectionOrderRef,
    selectionAnchorRef,
    setSelectedEntries,
    setSelectionOrder,
    setFocusedDocumentId,
  ]);

  useEffect(() => {
    if (!token) {
      setDropOverlayState((prev) => ({ ...prev, active: false }));
      dragCounterRef.current = 0;
      return undefined;
    }

    const handleDragEnter = (event) => {
      if (!hasFiles(event)) return;
      event.preventDefault();
      dragCounterRef.current += 1;
      setDropOverlayState({ active: true, folderName: currentFolderName });
    };

    const handleDragOver = (event) => {
      if (!hasFiles(event)) return;
      event.preventDefault();
      event.dataTransfer.dropEffect = 'copy';
    };

    const handleDragLeave = (event) => {
      if (!hasFiles(event)) return;
      dragCounterRef.current = Math.max(0, dragCounterRef.current - 1);
      if (dragCounterRef.current === 0) {
        setDropOverlayState((prev) => ({ ...prev, active: false }));
      }
    };

    const handleDrop = async (event) => {
      if (!hasFiles(event)) return;
      event.preventDefault();
      dragCounterRef.current = 0;
      setDropOverlayState((prev) => ({ ...prev, active: false }));
      await handleFileDrop(event.dataTransfer, selectedFolder);
    };

    const dropTarget = shellRef.current;
    if (!dropTarget) {
      return undefined;
    }

    dropTarget.addEventListener('dragenter', handleDragEnter);
    dropTarget.addEventListener('dragover', handleDragOver);
    dropTarget.addEventListener('dragleave', handleDragLeave);
    dropTarget.addEventListener('drop', handleDrop);

    return () => {
      dropTarget.removeEventListener('dragenter', handleDragEnter);
      dropTarget.removeEventListener('dragover', handleDragOver);
      dropTarget.removeEventListener('dragleave', handleDragLeave);
      dropTarget.removeEventListener('drop', handleDrop);
      dragCounterRef.current = 0;
      setDropOverlayState((prev) => ({ ...prev, active: false }));
    };
  }, [token, handleFileDrop, currentFolderName, selectedFolder]);

  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 handleLogout = useCallback(async () => {
    try {
      setLoading(true);
      await api.post('/auth/logout');
    } catch (error) {
      console.warn('[Auth] Failed to revoke refresh token during logout', error);
    } finally {
      setLoading(false);
      appDispatch({ type: 'LOGOUT' });
      setStatusMessage('Logged out.', 'info');
    }
  }, [appDispatch, setStatusMessage]);

  const folderClickHandlers = useMemo(
    () => ({
      onToggle: async (folderId) => {
        const node = folderNodes.get(folderId);
        const nextExpanded = !(node?.expanded ?? false);
        if (nextExpanded) {
          try {
            await ensureFolderData(folderId, {
              includeDocuments: false,
              prefetchDepth: 1,
            });
          } catch (error) {
            notifyApiError(error, 'Failed to load folder.');
          }
        } else if (node && !node.loaded) {
          try {
            await ensureFolderData(folderId, {
              includeDocuments: false,
              prefetchDepth: 1,
            });
          } catch (error) {
            notifyApiError(error, 'Failed to load folder.');
          }
        }
        setFolderNodes((prev) => {
          const next = new Map(prev);
          const current = next.get(folderId);
          if (!current) return prev;
          next.set(folderId, { ...current, expanded: nextExpanded });
          return next;
        });
      },
      onSelect: selectFolder,
      onDrop: async (event, folderId) => {
        event.preventDefault();
        event.stopPropagation();
        event.currentTarget.classList.remove('is-drop-target');

        let folderIds = [];
        try {
          const rawFolderList = event.dataTransfer.getData('application/x-papercrate-folder-list');
          if (rawFolderList) {
            const parsed = JSON.parse(rawFolderList);
            if (Array.isArray(parsed)) {
              folderIds = parsed.filter(Boolean);
            }
          }
        } catch (error) {
          console.warn('[folders] Failed to parse folder list drag payload', error);
        }

        if (!folderIds.length) {
          let folderSourceId = draggedFolderId;
          if (!folderSourceId) {
            try {
              if (event.dataTransfer.types?.includes('application/x-papercrate-folder')) {
                folderSourceId = event.dataTransfer.getData('application/x-papercrate-folder');
              }
            } catch (error) {
              console.warn('[folders] Failed to read folder id from drag payload', error);
            }
          }

          if (folderSourceId) {
            folderIds = [folderSourceId];
          }
        }

        folderIds = Array.from(new Set(folderIds.filter(Boolean)));

        if (folderIds.length) {
          setDraggedFolderId(null);
          const invalidMove = folderIds.some((sourceId) => isInvalidFolderDrop(sourceId, folderId));
          if (invalidMove) {
            setStatusMessage(
              'Cannot move a folder into itself or one of its descendants.',
              'error',
            );
            return;
          }

          for (const sourceId of folderIds) {
            // eslint-disable-next-line no-await-in-loop
            await moveFolder(sourceId, folderId);
          }
        }

        if (hasFiles(event)) {
          await handleFileDrop(event.dataTransfer, folderId);
          return;
        }

        let docIds = [];
        try {
          const raw = event.dataTransfer.getData('application/x-papercrate-doc-list');
          if (raw) {
            const parsed = JSON.parse(raw);
            if (Array.isArray(parsed)) {
              docIds = parsed.filter(Boolean);
            }
          }
        } catch (error) {
          console.warn('[documents] Failed to parse document list drag payload', error);
        }

        if (!docIds.length) {
          try {
            const single = event.dataTransfer.getData('application/x-papercrate-doc');
            if (single) {
              docIds = [single];
            }
          } catch (error) {
            console.warn('[documents] Failed to read single document drag payload', error);
          }
        }

        if (!docIds.length && draggedDocumentIds.length) {
          docIds = draggedDocumentIds;
        }

        docIds = Array.from(new Set(docIds));

        if (!docIds.length || folderId === selectedFolder) {
          return;
        }

        setDraggedDocumentIds([]);
        await moveDocumentsToFolder(docIds, folderId);
      },
      onDragOver: (event, folderId) => {
        const folderDragActive = Boolean(draggedFolderId);
        if (folderDragActive && isInvalidFolderDrop(draggedFolderId, folderId)) {
          return;
        }

        if (hasFiles(event)) {
          event.preventDefault();
          event.dataTransfer.dropEffect = 'copy';
          event.currentTarget.classList.add('is-drop-target');
          return;
        }

        if (draggedDocumentIds.length || folderDragActive) {
          event.preventDefault();
          event.dataTransfer.dropEffect = 'move';
          event.currentTarget.classList.add('is-drop-target');
        }
      },
      onDragLeave: (event) => {
        event.currentTarget.classList.remove('is-drop-target');
      },
    }),
    [
      draggedDocumentIds,
      draggedFolderId,
      ensureFolderData,
      folderNodes,
      handleFileDrop,
      isInvalidFolderDrop,
      moveDocumentsToFolder,
      moveFolder,
      notifyApiError,
      selectFolder,
      selectedFolder,
      setDraggedDocumentIds,
      setDraggedFolderId,
      setFolderNodes,
      setStatusMessage,
    ],
  );

  const selectedDocument = useMemo(() => {
    if (!focusedDocumentId) {
      return null;
    }
    const list = searchResults ?? documents;
    return list.find((doc) => doc.id === focusedDocumentId) || null;
  }, [searchResults, documents, focusedDocumentId]);


  const orderedSelectedDocuments = useMemo(() => {
    const ordered = [];
    const seen = new Set();
    const pushDoc = (doc) => {
      if (doc?.id && !seen.has(doc.id)) {
        ordered.push(doc);
        seen.add(doc.id);
      }
    };

    selectionOrder.forEach((key) => {
      if (!isDocumentRowKey(key)) {
        return;
      }
      const docId = getRowId(key);
      const doc = documentLookup.get(docId) || null;
      pushDoc(doc);
    });

    selectedDocumentIds.forEach((id) => {
      if (seen.has(id)) return;
      const doc = documentLookup.get(id) || null;
      pushDoc(doc);
    });

    return ordered;
  }, [selectionOrder, documentLookup, selectedDocumentIds]);

  const {
    detailPanelOpen,
    detailPanelSelectedDocuments,
    openDetailPanel,
    closeDetailPanel,
  } = useDetailPanel({
    selectedDocumentIds,
    documentLookup,
    orderedSelectedDocuments,
    selectionOrder,
    documentsViewMode,
    getRowId,
    isDocumentRowKey,
  });

  detailPanelControlRef.current = {
    open: openDetailPanel,
    close: closeDetailPanel,
  };

  const handleEntryPointer = useEntryPointerHandler({
    resolveDocumentRowKey,
    resolveFolderRowKey,
    onSelectDocument: (documentId, event, { modifierClick, primaryClick, rowKey }) => {
      const key = rowKey || resolveDocumentRowKey(documentId);
      if (key) {
        handleEntrySelection(key, event);
      }
      if (!modifierClick && primaryClick) {
        openDetailPanel({ documentIds: [documentId] });
      }
    },
    onSelectFolder: (folderId, event, { modifierClick, primaryClick, rowKey }) => {
      const key = rowKey || resolveFolderRowKey(folderId);
      if (key) {
        handleEntrySelection(key, event);
      }
      if (!modifierClick && primaryClick) {
        selectFolder(folderId);
      }
    },
  });

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

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

    const visited = new Set();

    orderedSelectedDocuments.forEach((doc) => {
      const folderId = doc?.folder_id;
      if (!folderId) {
        return;
      }
      let currentId = folderId;
      let guard = 0;
      while (currentId && currentId !== 'root' && guard < 32) {
        guard += 1;
        if (visited.has(currentId)) {
          break;
        }
        visited.add(currentId);
        const node = folderNodes.get(currentId);
        if (!node) {
          if (!detailFolderFetchRef.current.has(currentId)) {
            detailFolderFetchRef.current.add(currentId);
            ensureFolderData(currentId, { force: false, includeDocuments: false })
              .catch((error) => {
                console.warn('Failed to preload folder metadata for detail path', currentId, error);
              })
              .finally(() => {
                detailFolderFetchRef.current.delete(currentId);
              });
          }
          break;
        }

        const parentId = node.parentId ?? 'root';
        if (!parentId || parentId === 'root') {
          break;
        }
        currentId = parentId;
      }
    });
  }, [orderedSelectedDocuments, folderNodes, ensureFolderData]);

  const resolveFolderPath = useCallback(
    (folderId) => {
      if (!folderId || folderId === 'root') {
        return [];
      }

      const segments = [];
      const visited = new Set();
      let currentId = folderId;
      let guard = 0;

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

        if (currentId === 'root') {
          break;
        }

        const node = folderNodes.get(currentId);
        if (!node) {
          segments.push({ id: currentId, name: '…' });
          break;
        }

        segments.push({ id: node.id, name: node.name || 'Folder' });

        const parentId = node.parentId ?? 'root';
        if (!parentId || parentId === 'root') {
          segments.push({ id: 'root', name: DEFAULT_FOLDER_NAME });
          break;
        }

        currentId = parentId;
      }

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

      return segments.reverse();
    },
    [folderNodes],
  );

  const selectedPreviewEntry = useMemo(() => {
    if (!selectedDocument) {
      return null;
    }
    return previewEntries.get(selectedDocument.id) || null;
  }, [selectedDocument, previewEntries]);

  const previewWorkspaceEntry = useMemo(() => {
    if (!previewDocumentId) {
      return null;
    }
    return previewEntries.get(previewDocumentId) || null;
  }, [previewDocumentId, previewEntries]);

  const previewWorkspaceDocument = useMemo(() => {
    if (!previewDocumentId) return null;
    const pool = searchResults ?? documents;
    return pool.find((doc) => doc.id === previewDocumentId) || null;
  }, [previewDocumentId, searchResults, documents]);

  const previewActive = Boolean(previewDocumentId && previewWorkspaceDocument);

  const resolveThumbnailUrlForDoc = useCallback(
    (doc) =>
      resolveDocumentAssetUrl(doc, 'thumbnail', {
        ensureAssetUrl,
        getAsset: getDocumentAsset,
      }),
    [ensureAssetUrl, getDocumentAsset],
  );

  const handleDocumentsViewModeChange = useCallback((mode) => {
    const next = mode === 'grid' ? 'grid' : mode === 'desk' ? 'desk' : 'list';
    setDocumentsViewMode((previous) => {
      if (next !== previous && typeof window !== 'undefined') {
        try {
          window.sessionStorage.setItem('papercrate_view_mode', next);
        } catch (error) {
          console.warn('[view-mode] failed to persist mode', error);
        }
      }
      return next;
    });
  }, []);

  const handleDeskExit = useCallback(() => {
    const fallback = lastNonDeskViewRef.current && lastNonDeskViewRef.current !== 'desk'
      ? lastNonDeskViewRef.current
      : 'list';
    handleDocumentsViewModeChange(fallback);
  }, [handleDocumentsViewModeChange]);

  const handleTenantSelect = useCallback(
    async (tenantOption, { refreshOnly = false } = {}) => {
      const requestedTenantId = tenantOption?.id ?? null;
      if (!refreshOnly && (!requestedTenantId || requestedTenantId === currentTenantId)) {
        return;
      }

      setLoading(true);
      try {
        if (!refreshOnly) {
          setStatusMessage('Switching tenant…', 'info');
        }

        if (refreshOnly) {
          const { data } = await api.get('/auth/tenants');
          appDispatch({
            type: 'SET_TENANTS',
            tenants: Array.isArray(data?.tenants) ? data.tenants : [],
          });
          return;
        }

        const { data } = await api.post('/auth/select-tenant', { tenant_id: requestedTenantId });
        if (!data?.access_token) {
          throw new Error('Missing access token in tenant switch response.');
        }

        appDispatch({ type: 'LOGOUT' });
        resetWorkspaceState();

        appDispatch({
          type: 'LOGIN_SUCCESS',
          token: data.access_token,
          tenant: data.tenant || null,
        });

        api.defaults.headers.common.Authorization = `Bearer ${data.access_token}`;
        tokenRef.current = data.access_token;
        tenantIdRef.current = data?.tenant?.id ?? null;

        if (Array.isArray(data?.tenants)) {
          appDispatch({ type: 'SET_TENANTS', tenants: data.tenants });
        }

        handleDocumentsViewModeChange('list');
        navigate('/documents', { replace: true });

        await Promise.all([refreshTags(), refreshCorrespondents()]);
        await loadFolder('root', { showLoading: false, preserveSearch: false });

        const tenantLabel = data?.tenant?.name || data?.tenant?.id || 'tenant';
        setStatusMessage(`Switched to ${tenantLabel}.`, 'info');
      } catch (error) {
        notifyApiError(error, 'Failed to switch tenant.');
      } finally {
        setLoading(false);
      }
    },
    [
      currentTenantId,
      appDispatch,
      notifyApiError,
      setStatusMessage,
      resetWorkspaceState,
      handleDocumentsViewModeChange,
      navigate,
      refreshTags,
      refreshCorrespondents,
      loadFolder,
    ],
  );

  const documentsTableProps = useMemo(
    () => ({
      currentFolderName,
      breadcrumbs,
      onRefresh: refreshCurrentFolder,
      subfolders: currentSubfolders,
      documents,
      searchResults,
      isFilterActive,
      onFolderSelect: selectFolder,
      onFolderDrop: folderClickHandlers.onDrop,
      onFolderDragOver: folderClickHandlers.onDragOver,
      onFolderDragLeave: folderClickHandlers.onDragLeave,
      onFolderDragStart: handleFolderDragStart,
      onFolderDragEnd: handleFolderDragEnd,
      draggedFolderId,
      onFolderRename: handleFolderRename,
      onDocumentOpen: openDocumentPreview,
      onDocumentRename: handleDocumentTitleUpdate,
      selectedDocumentIds,
      selectedFolderIds,
      focusedRowKey,
      draggingDocumentIds: draggedDocumentIds,
      onDocumentDragStart: handleDocumentDragStart,
      onDocumentDragEnd: handleDocumentDragEnd,
      isSearchLoading: searchLoading,
      tagLookupById,
      activeCorrespondentIds: activeCorrespondentFilters,
      selectedEntries,
      onFocusedRowChange: setFocusedRowKey,
      ensureAssetUrl,
      getDocumentAsset,
      onTagClick: toggleTagFilter,
      onCorrespondentClick: toggleCorrespondentFilter,
      onDocumentTagDrop: handleDocumentTagDrop,
      viewMode: documentsViewMode,
      onViewModeChange: handleDocumentsViewModeChange,
      onClearSelection: clearDocumentSelection,
      onDeleteSelection: handleDeleteSelection,
      onEntryPointer: handleEntryPointer,
      onEntrySelection: handleEntrySelection,
      tags,
      correspondents,
      documentLookup,
      onBulkTagAdd: handleBulkTagAddFromDetail,
      onBulkTagRemove: handleBulkTagRemoveFromDetail,
      onBulkCorrespondentAdd: handleBulkCorrespondentAdd,
      onBulkCorrespondentRemove: handleBulkCorrespondentRemove,
      onBulkReanalyze: handleBulkSelectionReanalyze,
    }),
    [
      activeCorrespondentFilters,
      breadcrumbs,
      clearDocumentSelection,
      currentFolderName,
      currentSubfolders,
      documents,
      documentsViewMode,
      draggedDocumentIds,
      draggedFolderId,
      focusedRowKey,
      folderClickHandlers,
      handleDocumentDragEnd,
      handleDocumentDragStart,
      handleDocumentTagDrop,
      handleDocumentTitleUpdate,
      handleDocumentsViewModeChange,
      handleFolderDragEnd,
      handleFolderDragStart,
      handleFolderRename,
      handleEntryPointer,
      handleEntrySelection,
      handleDeleteSelection,
      isFilterActive,
      openDocumentPreview,
      refreshCurrentFolder,
      searchLoading,
      searchResults,
      selectFolder,
      selectedDocumentIds,
      selectedEntries,
      selectedFolderIds,
      setFocusedRowKey,
      tagLookupById,
      toggleCorrespondentFilter,
      toggleTagFilter,
      ensureAssetUrl,
      getDocumentAsset,
      tags,
      correspondents,
      documentLookup,
      handleBulkTagAddFromDetail,
      handleBulkTagRemoveFromDetail,
      handleBulkCorrespondentAdd,
      handleBulkCorrespondentRemove,
      handleBulkSelectionReanalyze,
    ],
  );

  const sidebarProps = useMemo(
    () => ({
      folderNodes,
      onToggle: folderClickHandlers.onToggle,
      onSelect: folderClickHandlers.onSelect,
      onDrop: folderClickHandlers.onDrop,
      onDragOver: folderClickHandlers.onDragOver,
      onDragLeave: folderClickHandlers.onDragLeave,
      onDeleteFolder: handleFolderDelete,
      onRenameFolder: handleFolderRename,
      selectedFolder,
      onFolderDragStart: handleFolderDragStart,
      onFolderDragEnd: handleFolderDragEnd,
      draggedFolderId,
      onCreateFolder: handlePromptCreateFolder,
      creatingFolder,
      tags,
      activeTagIds: activeTagFilters,
      onToggleTagFilter: toggleTagFilter,
      onCreateTag: (label) => handleTagCreate({ label }),
      correspondents,
      activeCorrespondentIds: activeCorrespondentFilters,
      onToggleCorrespondentFilter: toggleCorrespondentFilter,
      onCreateCorrespondent: (name) => handleCorrespondentCreate({ name }),
      appStatus,
      loading,
      previewActive,
      searchQuery,
      onSearchChange: handleSearchChange,
      onSearchSubmit: handleSearchSubmit,
      onSearchClear: clearFilters,
      isFilterActive,
      onLogout: handleLogout,
      status,
      tenantName,
      tenants: tenantOptions,
      activeTenantId: currentTenantId,
      onSelectTenant: handleTenantSelect,
      onOpenSettings: openSettings,
    }),
    [
      activeCorrespondentFilters,
      activeTagFilters,
      appStatus,
      clearFilters,
      correspondents,
      currentTenantId,
      folderClickHandlers,
      folderNodes,
      handleFolderDragEnd,
      handleFolderDragStart,
      handleFolderDelete,
      handleFolderRename,
      handleLogout,
      handleSearchChange,
      handleSearchSubmit,
      handleTenantSelect,
      loading,
      openSettings,
      previewActive,
      searchQuery,
      draggedFolderId,
      isFilterActive,
      selectedFolder,
      status,
      tags,
      tenantOptions,
      tenantName,
      toggleCorrespondentFilter,
      toggleTagFilter,
      handleTagCreate,
      handleCorrespondentCreate,
      handlePromptCreateFolder,
      creatingFolder,
    ],
  );

  const handleDetailPanelClose = useCallback(() => {
    closeDetailPanel();
  }, [closeDetailPanel]);

  const detailPanelProps = useMemo(
    () => ({
      selectedDocuments: detailPanelSelectedDocuments,
      tags,
      tagLookupById,
      onTagAdd: handleTagAdd,
      onTagRemove: handleTagRemove,
      previewEntry: selectedPreviewEntry,
      onOpenPreview: openDocumentPreview,
      onBulkTagAdd: handleBulkTagAddFromDetail,
      onBulkTagRemove: handleBulkTagRemoveFromDetail,
      onBulkCorrespondentAdd: handleBulkCorrespondentAdd,
      onBulkCorrespondentRemove: handleBulkCorrespondentRemove,
      onPromoteSelection: promoteSelectionOrder,
      activePreviewId,
      onUpdateTitle: handleDocumentTitleUpdate,
      onUpdateIssued: handleDocumentIssuedUpdate,
      ensureAssetUrl,
      getDocumentAsset,
      ensurePreviewData,
      correspondents,
      onCorrespondentAdd: handleCorrespondentAdd,
      onCorrespondentRemove: handleCorrespondentRemove,
      resolveApiPath,
      onFolderNavigate: selectFolder,
      onClose: handleDetailPanelClose,
      resolveFolderPath,
    }),
    [
      activePreviewId,
      correspondents,
      detailPanelSelectedDocuments,
      ensureAssetUrl,
      ensurePreviewData,
      getDocumentAsset,
      handleBulkCorrespondentAdd,
      handleBulkCorrespondentRemove,
      handleBulkTagAddFromDetail,
      handleBulkTagRemoveFromDetail,
      handleCorrespondentAdd,
      handleCorrespondentRemove,
      handleDetailPanelClose,
      handleDocumentTitleUpdate,
      handleDocumentIssuedUpdate,
      handleTagAdd,
      handleTagRemove,
      openDocumentPreview,
      promoteSelectionOrder,
      resolveFolderPath,
      selectFolder,
      selectedPreviewEntry,
      tags,
      tagLookupById,
    ],
  );

  const handleDeskInspectDocument = useCallback(
    (docId) => {
      if (!docId) {
        return;
      }
      const rowKey = resolveDocumentRowKey(docId);
      if (rowKey) {
        applySelection([rowKey], { anchor: rowKey, interactedKeys: [rowKey] });
      }
      openDetailPanel({ documentIds: [docId] });
    },
    [applySelection, openDetailPanel],
  );

  const handleDeskDocumentStackSelect = useCallback(
    (docIds) => {
      if (!Array.isArray(docIds) || docIds.length === 0) {
        return;
      }

      const rowKeys = docIds
        .map((id) => resolveDocumentRowKey(id))
        .filter(Boolean);

      if (!rowKeys.length) {
        return;
      }

      const nextKeys = [...selectedEntries];
      rowKeys.forEach((key) => {
        if (!nextKeys.includes(key)) {
          nextKeys.push(key);
        }
      });

      const anchor = rowKeys[0] || selectionAnchorRef.current || nextKeys[nextKeys.length - 1];

      applySelection(nextKeys, {
        anchor,
        interactedKeys: rowKeys,
      });
    },
    [applySelection, selectedEntries, selectionAnchorRef],
  );

  const handleDeskHelpOpen = useCallback(() => {
    setDeskHelpOpen(true);
  }, []);

  const handleDeskHelpClose = useCallback(() => {
    setDeskHelpOpen(false);
  }, []);

  const deskViewId = useMemo(() => {
    if (showingSearchResults) {
      const trimmedQuery = searchQuery.trim();
      const tagsKey = [...activeTagFilters].sort().join(',');
      const correspondentsKey = [...activeCorrespondentFilters].sort().join(',');
      return `search:${trimmedQuery}|tags:${tagsKey}|corr:${correspondentsKey}`;
    }
    const folderKey = selectedFolder && selectedFolder !== '' ? selectedFolder : 'root';
    return `folder:${folderKey}`;
  }, [
    showingSearchResults,
    searchQuery,
    activeTagFilters,
    activeCorrespondentFilters,
    selectedFolder,
  ]);

  const deskWorkspaceProps = useMemo(
    () => ({
      documents,
      searchResults,
      breadcrumbs,
      currentFolderName,
      viewMode: documentsViewMode,
      onViewModeChange: handleDocumentsViewModeChange,
      onExit: handleDeskExit,
      onRefresh: refreshCurrentFolder,
      onDocumentOpen: openDocumentPreview,
      onInspectDocument: handleDeskInspectDocument,
      onEntryPointer: handleEntryPointer,
      onDocumentStackSelect: handleDeskDocumentStackSelect,
      onPromoteSelection: promoteSelectionOrder,
      onOpenHelp: handleDeskHelpOpen,
      helpOpen: deskHelpOpen,
      onHelpClose: handleDeskHelpClose,
      tenantId: currentTenantId,
      viewId: deskViewId,
      selectedDocumentIds,
      selectedFolderIds,
      onClearSelection: clearDocumentSelection,
      detailPanelOpen,
      onCloseDetailPanel: handleDetailPanelClose,
      resolveThumbnailUrl: resolveThumbnailUrlForDoc,
      onAssignTagToDocument: handleDocumentTagAttach,
      onRemoveTagFromDocument: handleTagRemove,
      ensureAssetUrl,
      getDocumentAsset,
      activeTagIds: activeTagFilters,
      onDeleteSelection: handleDeleteSelection,
      tags,
      correspondents,
      documentLookup,
      tagLookupById,
      onBulkTagAdd: handleBulkTagAddFromDetail,
      onBulkTagRemove: handleBulkTagRemoveFromDetail,
      onBulkCorrespondentAdd: handleBulkCorrespondentAdd,
      onBulkCorrespondentRemove: handleBulkCorrespondentRemove,
      onBulkReanalyze: handleBulkSelectionReanalyze,
    }),
    [
      documents,
      searchResults,
      breadcrumbs,
      currentFolderName,
      documentsViewMode,
      handleDocumentsViewModeChange,
      handleDeskExit,
      refreshCurrentFolder,
      openDocumentPreview,
      handleDeskInspectDocument,
      handleDeskDocumentStackSelect,
      handleEntryPointer,
      promoteSelectionOrder,
      handleDeskHelpOpen,
      handleDeskHelpClose,
      currentTenantId,
      deskViewId,
      deskHelpOpen,
      selectedDocumentIds,
      selectedFolderIds,
      clearDocumentSelection,
      detailPanelOpen,
      handleDetailPanelClose,
      resolveThumbnailUrlForDoc,
      handleDocumentTagAttach,
      handleTagRemove,
      ensureAssetUrl,
      getDocumentAsset,
      activeTagFilters,
      handleDeleteSelection,
      tags,
      correspondents,
      documentLookup,
      tagLookupById,
      handleBulkTagAddFromDetail,
      handleBulkTagRemoveFromDetail,
      handleBulkCorrespondentAdd,
      handleBulkCorrespondentRemove,
      handleBulkSelectionReanalyze,
    ],
  );

  const contextValue = useMemo(
    () => ({
      token,
      appStatus,
      status,
      setStatusMessage,
      dropOverlayState,
      handleLogout,
      sidebarProps,
      tags,
      refreshTags,
      handleTagUpdate,
      handleTagDelete,
      handleDocumentTagAttach,
      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,
      resolveApiPath,
      getDocumentAsset,
      notifyApiError,
      openTagsModal,
      openCorrespondentsModal,
      openSettings,
      detailPanelOpen,
      openDetailPanel,
    }),
    [
      token,
      appStatus,
      status,
      setStatusMessage,
      dropOverlayState,
      handleLogout,
      sidebarProps,
      tags,
      refreshTags,
      handleTagUpdate,
      handleTagDelete,
      handleDocumentTagAttach,
      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,
      getDocumentAsset,
      notifyApiError,
      openTagsModal,
      openCorrespondentsModal,
      openSettings,
      detailPanelOpen,
      openDetailPanel,
    ],
  );

  if (['logged-out', 'authenticating', 'selecting-tenant'].includes(appStatus)) {
    const shouldRememberLastLocation = appStatus !== 'logged-out';
    return (
      <Navigate
        to="/account/login"
        replace
        state={
          shouldRememberLastLocation
            ? { from: location.pathname + location.search }
            : undefined
        }
      />
    );
  }

  return (
    <AppShellContext.Provider value={contextValue}>
      <div className="app-shell" ref={shellRef}>
        <DropOverlay
          active={dropOverlayState.active}
          folderName={dropOverlayState.folderName}
        />
        <Outlet />
        {managementModals}
      </div>
    </AppShellContext.Provider>
  );
};


export default AppLayout;
