import { useCallback, useEffect, useMemo } from 'react';
import type { MutableRefObject } from 'react';
import { resolveDocumentAssetUrl } from '../asset_manager';
import { useDetailPanel } from '../app/useDetailPanel';
import { DEFAULT_FOLDER_NAME } from '../app/workspaceUtils';
import { getEntryId, isDocumentEntry } from '../app/entryKey';
import type { DocumentInfoPanelProps } from '../documents/DocumentInfoPanel';
import type { EnsureAssetUrl, GetDocumentAsset } from '../utils/ocr';
import type { Identifier } from '../types/identifiers';
import type { Document } from '../types/documents';

interface FolderNode {
  id: Identifier | 'root';
  name?: string;
  parentId?: Identifier | 'root';
}

interface UseDetailWorkspaceArgs {
  documents: Document[];
  selectionOrder: string[];
  selectedDocumentIds: Identifier[];
  documentLookup: Map<Identifier, Document>;
  folderNodes: Map<Identifier | 'root', FolderNode>;
  ensureFolderData: (folderId: Identifier | 'root', options?: { force?: boolean; includeDocuments?: boolean }) => Promise<void>;
  detailPanelControlRef: MutableRefObject<{ open?: (args?: { documentIds?: Identifier[] }) => void; close?: () => void } | null>;
  detailFolderFetchRef: MutableRefObject<Set<Identifier | 'root'>>;
  previewDocumentId?: Identifier | null;
  activePreviewId?: Identifier | null;
  openDocumentPreview?: (args: { documentIds: Identifier[] }) => void;
  handleDocumentTitleUpdate?: (docId: Identifier, title: string) => Promise<boolean> | boolean;
  handleDocumentIssuedUpdate?: (docId: Identifier, issued: number | null) => Promise<boolean> | boolean;
  handleDocumentTagAdd?: (doc: Document, value: string, context?: { option?: unknown }) => void;
  handleTagRemove?: (...args: unknown[]) => void;
  ensureAssetUrl?: EnsureAssetUrl;
  getDocumentAsset?: GetDocumentAsset;
  correspondents?: unknown[];
  handleCorrespondentAdd?: (...args: unknown[]) => void;
  handleCorrespondentRemove?: (...args: unknown[]) => void;
  selectFolder?: (folderId?: Identifier | 'root') => void;
  tags?: unknown[];
  tagLookupById?: Map<Identifier, unknown> | null;
}

interface UseDetailWorkspaceResult {
  detailPanelProps: DocumentInfoPanelProps;
  detailPanelOpen: boolean;
  openDetailPanel: ReturnType<typeof useDetailPanel>['openDetailPanel'];
  closeDetailPanel: ReturnType<typeof useDetailPanel>['closeDetailPanel'];
  handleDetailPanelClose: () => void;
  inspectDocument: (docId: Identifier | null) => void;
  previewActive: boolean;
  previewWorkspaceDocument: Document | null;
  resolveThumbnailUrlForDoc: (doc: Document | null) => string | null;
  resolveFolderPath: (folderId?: Identifier | 'root') => Array<{ id: Identifier | 'root'; name: string }>;
}

const useDetailWorkspace = ({
  documents,
  selectionOrder,
  selectedDocumentIds,
  documentLookup,
  folderNodes,
  ensureFolderData,
  detailPanelControlRef,
  detailFolderFetchRef,
  previewDocumentId,
  activePreviewId,
  openDocumentPreview,
  handleDocumentTitleUpdate,
  handleDocumentIssuedUpdate,
  handleDocumentTagAdd,
  handleTagRemove,
  ensureAssetUrl,
  getDocumentAsset,
  correspondents,
  handleCorrespondentAdd,
  handleCorrespondentRemove,
  selectFolder,
  tags,
  tagLookupById,
}: UseDetailWorkspaceArgs): UseDetailWorkspaceResult => {
  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 (!isDocumentEntry(key)) {
        return;
      }
      const docId = getEntryId(key);
      const doc = documentLookup.get(docId) || null;
      pushDoc(doc);
    });

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

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

  const {
    detailPanelOpen,
    detailPanelDocument,
    openDetailPanel,
    closeDetailPanel,
  } = useDetailPanel({
    documentLookup,
    orderedSelectedDocuments,
  });

  useEffect(() => {
    detailPanelControlRef.current = {
      open: openDetailPanel,
      close: closeDetailPanel,
    };
  }, [detailPanelControlRef, openDetailPanel, closeDetailPanel]);

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

  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 previewWorkspaceDocument = useMemo(() => {
    if (!previewDocumentId) {
      return null;
    }
    return documentLookup.get(previewDocumentId)
      || documents.find((doc) => doc.id === previewDocumentId)
      || null;
  }, [previewDocumentId, documentLookup, documents]);

  const previewActive = Boolean(previewDocumentId && previewWorkspaceDocument);

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

  const inspectDocument = useCallback(
    (documentId) => {
      if (!documentId) {
        return;
      }
      openDetailPanel({ documentIds: [documentId] });
    },
    [openDetailPanel],
  );

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

  const detailPanelProps = useMemo(
    () => ({
      document: detailPanelDocument,
      tags,
      tagLookupById,
      onTagAdd: handleDocumentTagAdd,
      onTagRemove: handleTagRemove,
      onOpenPreview: openDocumentPreview,
      activePreviewId,
      onUpdateTitle: handleDocumentTitleUpdate,
      onUpdateIssued: handleDocumentIssuedUpdate,
      ensureAssetUrl,
      getDocumentAsset,
      correspondents,
      onCorrespondentAdd: handleCorrespondentAdd,
      onCorrespondentRemove: handleCorrespondentRemove,
      onFolderNavigate: selectFolder,
      onClose: handleDetailPanelClose,
      resolveFolderPath,
      folderNodes,
      ensureFolderData,
    }),
    [
      activePreviewId,
      correspondents,
      detailPanelDocument,
      ensureAssetUrl,
      getDocumentAsset,
      handleCorrespondentAdd,
      handleCorrespondentRemove,
      handleDetailPanelClose,
      handleDocumentTagAdd,
      handleDocumentIssuedUpdate,
      handleDocumentTitleUpdate,
      handleTagRemove,
      folderNodes,
      ensureFolderData,
      openDocumentPreview,
      resolveFolderPath,
      selectFolder,
      tags,
      tagLookupById,
    ],
  );

  return {
    detailPanelProps,
    detailPanelOpen,
    openDetailPanel,
    closeDetailPanel,
    handleDetailPanelClose,
    inspectDocument,
    previewActive,
    previewWorkspaceDocument,
    resolveThumbnailUrlForDoc,
    resolveFolderPath,
  };
};

export default useDetailWorkspace;
