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,
  getRowId,
  isDocumentRowKey,
} from '../app/appLayoutUtils';
import type { DocumentInfoPanelProps } from '../documents/DocumentInfoPanel';
import type { EnsureAssetUrl, GetDocumentAsset } from '../utils/ocr';

type Identifier = string | number;

interface DocumentLike {
  id?: Identifier;
  folder_id?: Identifier | 'root';
  title?: string;
  [key: string]: unknown;
}

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

type PreviewEntry = {
  url?: string;
  canGoPrev?: boolean;
  canGoNext?: boolean;
  goPrev?: () => void;
  goNext?: () => void;
} | null;

interface UseDetailWorkspaceArgs {
  documents: DocumentLike[];
  searchResults?: DocumentLike[] | null;
  previewDocuments?: Map<Identifier, DocumentLike> | null;
  selectionOrder: string[];
  selectedDocumentIds: Identifier[];
  documentLookup: Map<Identifier, DocumentLike>;
  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'>>;
  previewEntries: Map<Identifier, PreviewEntry>;
  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: DocumentLike, value: string, context?: { option?: unknown }) => void;
  handleTagRemove?: (...args: unknown[]) => void;
  ensureAssetUrl?: EnsureAssetUrl;
  getDocumentAsset?: GetDocumentAsset;
  ensurePreviewData?: (docId: Identifier, options?: Record<string, unknown>) => Promise<DocumentLike | null | undefined>;
  correspondents?: unknown[];
  handleCorrespondentAdd?: (...args: unknown[]) => void;
  handleCorrespondentRemove?: (...args: unknown[]) => void;
  resolveApiPath?: (path: string) => string;
  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: DocumentLike | null;
  previewWorkspaceEntry: PreviewEntry;
  resolveThumbnailUrlForDoc: (doc: DocumentLike | null) => string | null;
  resolveFolderPath: (folderId?: Identifier | 'root') => Array<{ id: Identifier | 'root'; name: string }>;
}

const useDetailWorkspace = ({
  documents,
  searchResults,
  previewDocuments,
  selectionOrder,
  selectedDocumentIds,
  documentLookup,
  folderNodes,
  ensureFolderData,
  detailPanelControlRef,
  detailFolderFetchRef,
  previewEntries,
  previewDocumentId,
  activePreviewId,
  openDocumentPreview,
  handleDocumentTitleUpdate,
  handleDocumentIssuedUpdate,
  handleDocumentTagAdd,
  handleTagRemove,
  ensureAssetUrl,
  getDocumentAsset,
  ensurePreviewData,
  correspondents,
  handleCorrespondentAdd,
  handleCorrespondentRemove,
  resolveApiPath,
  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 (!isDocumentRowKey(key)) {
        return;
      }
      const docId = getRowId(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 detailPanelPreviewEntry = useMemo(() => {
    if (!detailPanelDocument) {
      return null;
    }
    return previewEntries.get(detailPanelDocument.id) || null;
  }, [detailPanelDocument, 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)
      || previewDocuments?.get?.(previewDocumentId)
      || null;
  }, [previewDocumentId, searchResults, documents, previewDocuments]);

  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,
      previewEntry: detailPanelPreviewEntry,
      onOpenPreview: openDocumentPreview,
      activePreviewId,
      onUpdateTitle: handleDocumentTitleUpdate,
      onUpdateIssued: handleDocumentIssuedUpdate,
      ensureAssetUrl,
      getDocumentAsset,
        hydrateDocument: ensurePreviewData,
      correspondents,
      onCorrespondentAdd: handleCorrespondentAdd,
      onCorrespondentRemove: handleCorrespondentRemove,
      resolveApiPath,
      onFolderNavigate: selectFolder,
      onClose: handleDetailPanelClose,
      resolveFolderPath,
    }),
    [
      activePreviewId,
      correspondents,
      detailPanelDocument,
      ensureAssetUrl,
      ensurePreviewData,
      getDocumentAsset,
      handleCorrespondentAdd,
      handleCorrespondentRemove,
      handleDetailPanelClose,
      handleDocumentTagAdd,
      handleDocumentIssuedUpdate,
      handleDocumentTitleUpdate,
      handleTagRemove,
      openDocumentPreview,
      resolveApiPath,
      resolveFolderPath,
      selectFolder,
      detailPanelPreviewEntry,
      tags,
      tagLookupById,
    ],
  );

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

export default useDetailWorkspace;
