import { useCallback, useEffect, useRef } from 'react';
import type {
  Dispatch,
  MutableRefObject,
  SetStateAction,
} from 'react';
import { useNavigate } from 'react-router-dom';
import type { DocumentId } from '../types/identifiers';

type FolderId = DocumentId | 'root';

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

interface UseDocumentPreviewArgs {
  routeDocumentId?: DocumentId | null;
  documentsManager: {
    getById: (id: DocumentId) => Document | null;
    ensure: (id: DocumentId) => Promise<Document | null>;
    getMany: (ids: DocumentId[]) => Document[];
    subscribe: (listener: () => void) => () => void;
    ingest: (docs: unknown[]) => { canonical: Document[]; changed: boolean };
  };
  selectedFolder?: FolderId | null;
  locationPathname: string;
  locationSearch: string;
  detailPanelControlRef: MutableRefObject<{
    open?: (args?: { documentIds?: DocumentId[] }) => void;
    close?: () => void;
  } | null>;
  setActivePreviewId: Dispatch<SetStateAction<DocumentId | null>>;
}

interface UseDocumentPreviewResult {
  ensurePreviewData: (documentId: DocumentId) => Promise<Document | null>;
  openDocumentPreview: (documentId: DocumentId, options?: { replace?: boolean }) => void;
  closeDocumentPreview: (folderId?: FolderId) => void;
  resetPreviewState: () => void;
}

const useDocumentPreview = ({
  routeDocumentId,
  documentsManager,
  selectedFolder,
  locationPathname,
  locationSearch,
  detailPanelControlRef,
  setActivePreviewId,
}: UseDocumentPreviewArgs): UseDocumentPreviewResult => {
  const previewReturnPathRef = useRef<string | null>(null);
  const notifyApiError = useNotifyApiError();
  const navigate = useNavigate();

  const resetPreviewState = useCallback(() => {
    previewReturnPathRef.current = null;
  }, []);

  const ensurePreviewData = useCallback(
    async (documentId: DocumentId): Promise<Document | null> => {
      if (!documentId) return null;

      const doc = await documentsManager.ensure(documentId);

      if (!doc) {
        throw new Error('Document metadata unavailable.');
      }

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

      setActivePreviewId(documentId);
      return doc;
    },
    [
      documentsManager,
      setActivePreviewId,
    ],
  );

  const openDocumentPreview = useCallback(
    (documentId: DocumentId, { replace = false }: { replace?: boolean } = {}) => {
      if (!documentId) return;
      detailPanelControlRef.current?.close?.();
      previewReturnPathRef.current = `${locationPathname}${locationSearch}`;
      navigate(`/documents/${documentId}`, { replace });
    },
    [navigate, locationPathname, locationSearch, detailPanelControlRef],
  );

  const closeDocumentPreview = useCallback(
    (folderId?: FolderId) => {
      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],
  );

  useEffect(() => {
    if (!routeDocumentId) {
      return undefined;
    }

    let cancelled = false;

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

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

  return {
    ensurePreviewData,
    openDocumentPreview,
    closeDocumentPreview,
    resetPreviewState,
  };
};

export default useDocumentPreview;
