import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ViewListIcon, ViewGridIcon, IconFileStack } from '../../ui/icons';
import BreadcrumbTrail from '../../ui/BreadcrumbTrail';
import DocumentsGrid from '../DocumentsGrid';
import DocumentsList from '../DocumentsList';
import { isTagTransferEvent } from '../tagTransfer';
import PreviewZoomOverlay from '../../detail/PreviewZoomOverlay';
import { useAssetNavigator } from '../../hooks/useAssetNavigator';
import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer';

const DEFAULT_GRID_ICON_SIZE = 144;

const EntryType = {
  folder: 'folder',
  document: 'document',
};

interface DocumentsPanelProps {
  [key: string]: any;
}

const defaultGetDocumentAsset = (_doc?: unknown, _type?: string) => null;

const DocumentsPanel: React.FC<DocumentsPanelProps> = ({
  currentFolderName,
  breadcrumbs,
  onRefresh,
  subfolders,
  documents,
  searchResults,
  isFilterActive = false,
  onFolderSelect,
  onFolderDrop,
  onFolderDragOver,
  onFolderDragLeave,
  onFolderDragStart,
  onFolderDragEnd,
  draggedFolderId,
  onFolderRename,
  selectedFolderIds = [],
  selectedDocumentIds = [],
  focusedRowKey,
  draggingDocumentIds = [],
  onDocumentDragStart,
  onDocumentDragEnd,
  onDocumentRename,
  onEntryPointer = null,
  onEntrySelection = null,
  onInspectDocument = null,
  tagLookupById,
  activeCorrespondentIds = [],
  onFocusedRowChange,
  ensureAssetUrl = null,
  getDocumentAsset = defaultGetDocumentAsset,
  onTagClick,
  onCorrespondentClick,
  isSearchLoading = false,
  onDocumentTagDrop,
  viewMode = 'list',
  onViewModeChange,
  onClearSelection,
  selectedEntries = [],
  showHeader = true,
}) => {
  const showingSearchResults = searchResults !== null;
  const rows = showingSearchResults ? searchResults : documents;

  const currentFolderId = useMemo(() => {
    if (showingSearchResults) {
      return null;
    }
    const trail = Array.isArray(breadcrumbs) ? breadcrumbs : [];
    if (trail.length === 0) {
      return 'root';
    }
    return trail[trail.length - 1]?.id || 'root';
  }, [breadcrumbs, showingSearchResults]);

  const selectionContextRef = useRef(null);
  useEffect(() => {
    const nextContext = showingSearchResults
      ? { type: 'search', marker: searchResults }
      : { type: 'folder', marker: currentFolderId || 'root' };
    const previous = selectionContextRef.current;
    selectionContextRef.current = nextContext;
    if (!previous) {
      return;
    }
    const changed = previous.type !== nextContext.type
      || previous.marker !== nextContext.marker;
    if (changed) {
      onClearSelection?.();
    }
  }, [showingSearchResults, currentFolderId, searchResults, onClearSelection]);

  const entries = useMemo(() => {
    const list = [];
    if (!showingSearchResults) {
      subfolders.forEach((folder) => {
        if (!folder || !folder.id) {
          return;
        }
        list.push({ type: EntryType.folder, id: folder.id, key: `folder:${folder.id}`, folder });
      });
    }
    rows.forEach((doc) => {
      if (!doc || !doc.id) {
        return;
      }
      list.push({ type: EntryType.document, id: doc.id, key: `document:${doc.id}`, document: doc });
    });
    return list;
  }, [showingSearchResults, subfolders, rows]);

  const selectedSet = useMemo(
    () => new Set(selectedDocumentIds),
    [selectedDocumentIds],
  );
  const selectedFolderSet = useMemo(
    () => new Set(selectedFolderIds || []),
    [selectedFolderIds],
  );
  const draggingSet = useMemo(
    () => new Set(draggingDocumentIds || []),
    [draggingDocumentIds],
  );
  const activeCorrespondentIdSet = useMemo(
    () => new Set(activeCorrespondentIds || []),
    [activeCorrespondentIds],
  );
  const scrollRef = useRef(null);
  const suppressDocumentClickRef = useRef(false);
  const [, forceVisibilityTick] = useState(0);
  const lastScrollNodeRef = useRef(null);
  const assignScrollRef = useCallback((node) => {
    if (lastScrollNodeRef.current === node) {
      return;
    }
    lastScrollNodeRef.current = node;
    scrollRef.current = node;
    if (node) {
      forceVisibilityTick((value) => value + 1);
    }
  }, []);
  const isGridView = viewMode === 'grid';
  const isDeskView = viewMode === 'desk';

  const [previewDocId, setPreviewDocId] = useState(null);

  const previewDoc = useMemo(() => {
    if (!previewDocId) {
      return null;
    }
    return rows.find((doc) => doc?.id === previewDocId) || null;
  }, [previewDocId, rows]);

  useEffect(() => {
    if (previewDocId && !previewDoc) {
      setPreviewDocId(null);
    }
  }, [previewDocId, previewDoc]);

  const previewNavigator = useAssetNavigator({
    document: previewDoc,
    assetType: 'preview',
    ensureAssetUrl,
    getAsset: getDocumentAsset,
    prefetch: 3,
  });

  const {
    currentUrl: previewUrl,
    canGoPrev: previewCanGoPrev,
    canGoNext: previewCanGoNext,
    goPrev: previewGoPrev,
    goNext: previewGoNext,
  } = previewNavigator;

  const previewDisplay = useMemo(() => {
    if (!previewDoc || !previewUrl) {
      return null;
    }
    return {
      url: previewUrl,
      alt: previewDoc.title,
      canGoPrev: Boolean(previewCanGoPrev),
      canGoNext: Boolean(previewCanGoNext),
      goPrev: previewGoPrev,
      goNext: previewGoNext,
    };
  }, [previewDoc, previewUrl, previewCanGoPrev, previewCanGoNext, previewGoPrev, previewGoNext]);

  const closePreviewOverlay = useCallback(() => {
    setPreviewDocId(null);
  }, []);

  const handleDocumentPreviewZoom = useCallback(
    (doc) => {
      if (!doc || !doc.id) {
        return;
      }
      const previewAsset = typeof getDocumentAsset === 'function' ? getDocumentAsset(doc, 'preview') : null;
      if (!previewAsset) {
        return;
      }
      setPreviewDocId(doc.id);
    },
    [getDocumentAsset],
  );

  const handleDocumentActivate = useCallback(
    (doc, event?: React.MouseEvent | KeyboardEvent | null) => {
      if (!doc) {
        return;
      }
      if (event) {
        if (typeof event.preventDefault === 'function') {
          event.preventDefault();
        }
        if (typeof event.stopPropagation === 'function') {
          event.stopPropagation();
        }
      }
      if (event?.altKey) {
        handleDocumentPreviewZoom(doc);
        return;
      }
      onInspectDocument?.(doc.id);
    },
    [handleDocumentPreviewZoom, onInspectDocument],
  );

  const navigableRows = useMemo(
    () => entries.map((entry) => ({ key: entry.key, type: entry.type, id: entry.id })),
    [entries],
  );
  const navigableRowKeys = useMemo(() => navigableRows.map((row) => row.key), [navigableRows]);

  const getEntryByKey = useCallback(
    (rowKey) => entries.find((entry) => entry.key === rowKey) || null,
    [entries],
  );

  const handlePanelFocus = useCallback(() => {
    let resolvedKey = null;

    if (focusedRowKey && navigableRowKeys.includes(focusedRowKey)) {
      resolvedKey = focusedRowKey;
    }

    if (!resolvedKey) {
      for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
        const candidate = selectedEntries[index];
        if (navigableRowKeys.includes(candidate)) {
          resolvedKey = candidate;
          break;
        }
      }
    }

    if (!resolvedKey) {
      if (!selectedEntries.length) {
        return;
      }
      if (navigableRows.length) {
        resolvedKey = navigableRows[0].key;
      }
    }

    if (!resolvedKey) {
      return;
    }

    onFocusedRowChange?.(resolvedKey);
  }, [
    focusedRowKey,
    navigableRowKeys,
    navigableRows,
    onFocusedRowChange,
    selectedEntries,
  ]);

  const handlePanelKeyDown = useCallback(
    (event) => {
      const { key, shiftKey } = event;
      const triggers = ['ArrowUp', 'ArrowDown', 'Home', 'End', 'Enter', ' ', 'Space', 'Spacebar'];
      if (!triggers.includes(key)) {
        return;
      }

      if (!navigableRows.length) {
        return;
      }

      event.preventDefault();

      let activeKey =
        focusedRowKey && navigableRowKeys.includes(focusedRowKey)
          ? focusedRowKey
          : null;

      if (!activeKey) {
        if (selectedEntries.length) {
          for (let index = selectedEntries.length - 1; index >= 0; index -= 1) {
            const candidate = selectedEntries[index];
            if (navigableRowKeys.includes(candidate)) {
              activeKey = candidate;
              break;
            }
          }
        }

        if (!activeKey) {
          activeKey = key === 'ArrowUp' ? navigableRowKeys[navigableRowKeys.length - 1] : navigableRowKeys[0];
        }
      }

      const currentIndex = navigableRowKeys.indexOf(activeKey);
      const activeRow = currentIndex === -1 ? null : navigableRows[currentIndex];

      if (key === 'Enter' || key === ' ' || key === 'Space' || key === 'Spacebar') {
        if (activeRow) {
          onEntrySelection?.(activeRow.key, event);
          if (activeRow.type === EntryType.folder) {
            onFolderSelect?.(activeRow.id);
          } else {
            const entry = getEntryByKey(activeRow.key);
            if (entry?.document) {
              handleDocumentPreviewZoom(entry.document);
            }
          }
        }
        return;
      }

      let nextIndex = currentIndex;
      if (key === 'ArrowDown') {
        nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, navigableRows.length - 1);
      } else if (key === 'ArrowUp') {
        nextIndex = currentIndex === -1 ? navigableRows.length - 1 : Math.max(currentIndex - 1, 0);
      } else if (key === 'Home') {
        nextIndex = 0;
      } else if (key === 'End') {
        nextIndex = navigableRows.length - 1;
      }

      if (nextIndex === -1 || nextIndex >= navigableRows.length) {
        return;
      }

      const targetRow = navigableRows[nextIndex];
      if (!targetRow) {
        return;
      }

      onFocusedRowChange?.(targetRow.key);
      onEntrySelection?.(targetRow.key, {
        shiftKey,
        preventDefault: () => {},
      });
    },
    [
      focusedRowKey,
      getEntryByKey,
      navigableRowKeys,
      navigableRows,
      onEntrySelection,
      onFocusedRowChange,
      onFolderSelect,
      selectedEntries,
      handleDocumentPreviewZoom,
    ],
  );

  const isListView = viewMode === 'list';
  const gridIconSize = DEFAULT_GRID_ICON_SIZE;
  const handleSetViewMode = useCallback(
    (nextMode) => {
      if (!onViewModeChange) {
        return;
      }
      onViewModeChange(nextMode);
      if (scrollRef.current) {
        scrollRef.current.scrollTop = 0;
      }
    },
    [onViewModeChange],
  );
  useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTop = 0;
    }
  }, [viewMode]);
  const isTagDragEvent = useCallback((event) => isTagTransferEvent(event), []);
  const ensureFocusedRowVisible = useCallback(() => {
    if (!focusedRowKey) return;
    const container = scrollRef.current;
    if (!container) return;
    let selector = null;
    if (focusedRowKey.startsWith('document:')) {
      selector = `#document-row-${focusedRowKey.slice('document:'.length)}`;
    } else if (focusedRowKey.startsWith('folder:')) {
      selector = `#folder-row-${focusedRowKey.slice('folder:'.length)}`;
    }
    if (!selector) {
      return;
    }
    const row = container.querySelector(selector);
    if (!row || !container.contains(row)) {
      return;
    }

    const header = container.querySelector('thead');
    const headerHeight = header ? header.getBoundingClientRect().height : 0;
    const rowTop = row.offsetTop;
    const rowBottom = rowTop + row.offsetHeight;
    const visibleTop = container.scrollTop + headerHeight;
    const visibleBottom = container.scrollTop + container.clientHeight;

    if (rowTop < visibleTop) {
      container.scrollTop = Math.max(rowTop - headerHeight, 0);
      return;
    }

    if (rowBottom > visibleBottom) {
      const nextScrollTop = rowBottom - container.clientHeight;
      container.scrollTop = Math.max(nextScrollTop, 0);
    }
  }, [focusedRowKey]);

  useEffect(() => {
    ensureFocusedRowVisible();
  }, [ensureFocusedRowVisible]);

  const activeDescendantId = useMemo(() => {
    if (!focusedRowKey) return undefined;
    if (focusedRowKey.startsWith('document:')) {
      return `document-row-${focusedRowKey.slice('document:'.length)}`;
    }
    if (focusedRowKey.startsWith('folder:')) {
      return `folder-row-${focusedRowKey.slice('folder:'.length)}`;
    }
    return undefined;
  }, [focusedRowKey]);

  const handleDocumentTagDragOver = useCallback(
    (event) => {
      if (!isTagDragEvent(event)) {
        return;
      }
      event.preventDefault();
      event.dataTransfer.dropEffect = 'copy';
      event.currentTarget.classList.add('tag-drop-target');
    },
    [isTagDragEvent],
  );

  const handleDocumentTagDragLeave = useCallback(
    (event) => {
      if (!isTagDragEvent(event)) {
        return;
      }
      if (event.relatedTarget && event.currentTarget.contains(event.relatedTarget)) {
        return;
      }
      event.currentTarget.classList.remove('tag-drop-target');
    },
    [isTagDragEvent],
  );

  const handleDocumentTagDrop = useCallback(
    (event, documentId) => {
      if (!isTagDragEvent(event)) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      event.currentTarget.classList.remove('tag-drop-target');
      const payload =
        event.dataTransfer.getData('application/x-papercrate-tag') ||
        event.dataTransfer.getData('text/papercrate-tag');
      if (!payload) {
        return;
      }
      try {
        const parsed = JSON.parse(payload);
        if (parsed?.id && onDocumentTagDrop) {
          onDocumentTagDrop(documentId, parsed);
        }
      } catch (error) {
        console.warn('[documents] Failed to parse tag drop payload', error);
      }
    },
    [isTagDragEvent, onDocumentTagDrop],
  );

  const handleDocumentClick = useCallback(
    (doc, event) => {
      if (!doc || suppressDocumentClickRef.current) {
        return;
      }

      if (typeof onEntryPointer === 'function') {
        onEntryPointer(
          { type: EntryType.document, id: doc.id, key: `document:${doc.id}`, document: doc },
          event,
        );
      }
    },
    [onEntryPointer],
  );

  const handleFolderClick = useCallback(
    (folder, event) => {
      if (!folder) {
        return;
      }

      if (typeof onEntryPointer === 'function') {
        onEntryPointer(
          { type: EntryType.folder, id: folder.id, key: `folder:${folder.id}`, folder },
          event,
        );
      }

      if (
        !isPointerModifierEvent(event)
        && isPrimaryPointerEvent(event)
        && scrollRef.current
      ) {
        scrollRef.current.focus({ preventScroll: true });
        onFocusedRowChange?.(`folder:${folder.id}`);
      }
    },
    [onEntryPointer, onFocusedRowChange],
  );

  const handleDocumentDragStartLocal = useCallback(
    (event, doc) => {
      suppressDocumentClickRef.current = true;
      onDocumentDragStart?.(event, doc);
    },
    [onDocumentDragStart],
  );

  const handleDocumentDragEndLocal = useCallback(
    (event) => {
      onDocumentDragEnd?.(event);
      requestAnimationFrame(() => {
        suppressDocumentClickRef.current = false;
      });
    },
    [onDocumentDragEnd],
  );

  const hasDocumentEntries = useMemo(
    () => entries.some((entry) => entry.type === EntryType.document),
    [entries],
  );
  const showTableRows = entries.length > 0;
  const showDefaultEmptyState = !showingSearchResults && !isFilterActive && entries.length === 0;
  const showListSearchEmptyState = showingSearchResults && !hasDocumentEntries && !isGridView && !isSearchLoading;
  const showGridSearchEmptyState = isGridView && showingSearchResults && !hasDocumentEntries && !isSearchLoading;
  const breadcrumbEntries = useMemo(() => (Array.isArray(breadcrumbs) ? breadcrumbs.filter(Boolean) : []), [breadcrumbs]);
  const trailEntries = useMemo(() => {
    if (!breadcrumbEntries.length) {
      return [{ id: 'current-folder', label: currentFolderName }];
    }
    const lastIndex = breadcrumbEntries.length - 1;
    return breadcrumbEntries.map((crumb, index) => ({
      id: crumb.id ?? index,
      label: crumb.name ?? crumb.label ?? crumb.title ?? '',
      onClick: index < lastIndex && onFolderSelect
        ? () => onFolderSelect(crumb.id)
        : null,
    }));
  }, [breadcrumbEntries, currentFolderName, onFolderSelect]);

  return (
    <>
      <section
      className={`documents-panel documents-panel--view-${isGridView ? 'grid' : 'list'}`}
    >
      {showHeader ? (
        <div className="panel-section__header">
          <div className="panel-section__titles">
            <h2 className="documents-panel__title">
              <BreadcrumbTrail
                entries={trailEntries}
                className="documents-panel__breadcrumbs"
                separator="/"
              />
            </h2>
            {showingSearchResults && (
              <div className="panel-section__subtitle">Search results</div>
            )}
          </div>
          <div className="header-actions">
            <div className="view-toggle" role="group" aria-label="Change view">
              <button
                type="button"
                className={`view-toggle__button${isListView ? ' active' : ''}`}
                onClick={() => handleSetViewMode('list')}
                aria-pressed={isListView}
                title="List view"
              >
                <ViewListIcon className="view-toggle__icon" size={18} />
              </button>
              <button
                type="button"
                className={`view-toggle__button${isGridView ? ' active' : ''}`}
                onClick={() => handleSetViewMode('grid')}
                aria-pressed={isGridView}
                title="Icons view"
              >
                <ViewGridIcon className="view-toggle__icon" size={18} />
              </button>
              <button
                type="button"
                className={`view-toggle__button${isDeskView ? ' active' : ''}`}
                onClick={() => handleSetViewMode('desk')}
                aria-pressed={isDeskView}
                title="Desk view"
              >
                <IconFileStack className="view-toggle__icon" size={18} />
              </button>
            </div>
            <button className="secondary" onClick={onRefresh}>
              Refresh
            </button>
          </div>
        </div>
      ) : null}
      {showDefaultEmptyState ? (
        <div className="panel-section__body">
          <div className="empty-state">
            Drop files anywhere or onto a folder to upload documents.
          </div>
        </div>
      ) : showGridSearchEmptyState ? (
        <div className="panel-section__body">
          <div className="empty-state empty-state--global">
            No documents match the current filters.
          </div>
        </div>
      ) : showListSearchEmptyState ? (
        <div className="panel-section__body">
          <div className="empty-state">No documents match the current filters.</div>
        </div>
      ) : (
        <div className="panel-section__body">
          <div
            ref={assignScrollRef}
            className="documents-scroll"
            tabIndex={0}
            onFocus={(event) => {
              if (event.target === scrollRef.current) {
                handlePanelFocus();
              }
            }}
            onKeyDown={(event) => {
              if (event.target !== scrollRef.current) {
                return;
              }
              handlePanelKeyDown(event);
            }}
            onClick={(event) => {
              if (event.target === event.currentTarget) {
                onClearSelection?.();
              }
            }}
            aria-activedescendant={isGridView ? undefined : activeDescendantId}
          >
            {isGridView ? (
              <DocumentsGrid
                entries={entries}
                selectedDocumentIdsSet={selectedSet}
                selectedFolderIdsSet={selectedFolderSet}
                draggingDocumentIdsSet={draggingSet}
                draggedFolderId={draggedFolderId}
                onFolderClick={handleFolderClick}
                onFolderSelect={onFolderSelect}
                onFolderDragOver={onFolderDragOver}
                onFolderDragLeave={onFolderDragLeave}
                onFolderDrop={onFolderDrop}
                onFolderDragStart={onFolderDragStart}
                onFolderDragEnd={onFolderDragEnd}
                onDocumentClick={handleDocumentClick}
                onDocumentActivate={handleDocumentActivate}
                onDocumentDragStart={handleDocumentDragStartLocal}
                onDocumentDragEnd={handleDocumentDragEndLocal}
                onDocumentTagDragOver={handleDocumentTagDragOver}
                onDocumentTagDragLeave={handleDocumentTagDragLeave}
                onDocumentTagDrop={handleDocumentTagDrop}
                ensureAssetUrl={ensureAssetUrl}
                getDocumentAsset={getDocumentAsset}
                gridIconSize={gridIconSize}
                tagLookupById={tagLookupById}
                onTagClick={onTagClick}
                scrollRef={scrollRef}
                onCorrespondentClick={onCorrespondentClick}
                activeCorrespondentIdSet={activeCorrespondentIdSet}
                onClearSelection={onClearSelection}
                onDocumentRename={onDocumentRename}
                onFolderRename={onFolderRename}
              />
            ) : !showTableRows ? null : (
              <DocumentsList
                entries={entries}
                focusedRowKey={focusedRowKey}
                selectedDocumentIdsSet={selectedSet}
                selectedFolderIdsSet={selectedFolderSet}
                draggingDocumentIdsSet={draggingSet}
                draggedFolderId={draggedFolderId}
                ensureAssetUrl={ensureAssetUrl}
                getDocumentAsset={getDocumentAsset}
                onFolderClick={handleFolderClick}
                onFolderSelect={onFolderSelect}
                onFolderDragOver={onFolderDragOver}
                onFolderDragLeave={onFolderDragLeave}
                onFolderDrop={onFolderDrop}
                onFolderDragStart={onFolderDragStart}
                onFolderDragEnd={onFolderDragEnd}
                onFolderRename={onFolderRename}
                onDocumentClick={handleDocumentClick}
                onDocumentActivate={handleDocumentActivate}
                onDocumentDragStart={handleDocumentDragStartLocal}
                onDocumentDragEnd={handleDocumentDragEndLocal}
                onDocumentTagDragOver={handleDocumentTagDragOver}
                onDocumentTagDragLeave={handleDocumentTagDragLeave}
                onDocumentTagDrop={handleDocumentTagDrop}
                onDocumentRename={onDocumentRename}
                tagLookupById={tagLookupById}
                onTagClick={onTagClick}
                onCorrespondentClick={onCorrespondentClick}
                activeCorrespondentIdSet={activeCorrespondentIdSet}
                scrollRef={scrollRef}
                onClearSelection={onClearSelection}
              />
            )}
          </div>
        </div>
      )}
    </section>
      <PreviewZoomOverlay
        open={Boolean(previewDocId)}
        display={previewDisplay}
        onClose={closePreviewOverlay}
      />
    </>
  );
};

export default DocumentsPanel;
