import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { DocumentsList, DocumentsGrid } from '../DocumentsView';
import type { DragEvent, ReactNode, RefObject } from 'react';
import type {
  DocumentsListEntry,
  FolderEventHandler,
  DocumentEventHandler,
  Document,
  DocumentTag,
} from '../../types/documents';
import DesktopWorkspace from '../../desktop/DesktopWorkspace';
import { isTagTransferEvent } from '../tagTransfer';
import { usePreviewContext } from '../../preview/PreviewContext';
import { isPointerModifierEvent, isPrimaryPointerEvent } from '../useEntryPointer';
import {
  WorkspaceSelectionProvider,
  useWorkspaceSelectionContext,
} from '../../app/WorkspaceSelectionContext';
import type { WorkspaceSelectionValue } from '../../app/WorkspaceSelectionContext';
import DocumentsPanelHeader, {
  DocumentsPanelHeaderConfig,
  DocumentsHeaderBreadcrumb,
} from './DocumentsPanelHeader';
import { SelectionFloatingPanel } from '../SelectionFloatingActions';
import { createDocumentsTableHeaderActions } from './DocumentsToolbar';
import { useDocumentsFilter } from '../context/DocumentsFilterContext';
import { DEFAULT_GRID_ICON_SIZE } from '../../constants/documents';
import type { Identifier } from '../../types/identifiers';

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

interface DocumentsPanelInnerProps {
  headerLeading?: ReactNode;
  onBreadcrumbNavigate?: (crumb: DocumentsHeaderBreadcrumb) => void;
  [key: string]: any;
}

interface DocumentsPanelProps extends DocumentsPanelInnerProps {
  selectionValue: WorkspaceSelectionValue;
}

export type DocumentLinkLike = { url?: string | null; mimeType?: string | null };

export interface DocumentsViewProps {
  entries: DocumentsListEntry[];
  draggingDocumentIdsSet?: Set<Identifier> | null;
  draggedFolderId?: Identifier | 'root' | null;
  ensureAssetUrl?: (...args: any[]) => unknown;
  getDocumentAsset?: (...args: any[]) => unknown;
  onFolderClick?: FolderEventHandler;
  onFolderSelect?: (folderId: Identifier | 'root') => void;
  onFolderDragOver?: (event: DragEvent<HTMLElement>, folderId: Identifier | 'root') => void;
  onFolderDragLeave?: (event: DragEvent<HTMLElement>) => void;
  onFolderDrop?: (event: DragEvent<HTMLElement>, folderId: Identifier | 'root') => void;
  onFolderDragStart?: (event: DragEvent<HTMLElement>, folderId: Identifier | 'root') => void;
  onFolderDragEnd?: (event: DragEvent<HTMLElement>) => void;
  onFolderRename?: (folderId: Identifier | 'root', nextName: string) => Promise<boolean> | boolean;
  onDocumentOpen?: DocumentEventHandler;
  onDocumentActivate?: (doc: Document, event?: unknown) => void;
  onDocumentDragStart?: (event: DragEvent<HTMLElement>, document: Document) => void;
  onDocumentDragEnd?: (event: DragEvent<HTMLElement>) => void;
  onDocumentTagDragStart?: (event: DragEvent<HTMLElement>, docId: Identifier, tagId: Identifier) => void;
  onDocumentTagDragEnd?: (event: DragEvent<HTMLElement>) => void;
  onDocumentTagDragOver?: (event: DragEvent<HTMLElement>, docId: Identifier) => void;
  onDocumentTagDragLeave?: (event: DragEvent<HTMLElement>) => void;
  onDocumentTagAttach?: (documentId: Identifier, tagId: Identifier) => void;
  onDocumentTagDetach?: (documentId: Identifier, tagId: Identifier) => void;
  onDocumentRename?: (documentId: Identifier, nextTitle: string) => Promise<boolean> | boolean;
  tagLookupById?: Map<Identifier, DocumentTag> | null;
  onTagClick?: (tagId: Identifier) => void;
  onCorrespondentClick?: (correspondentId: Identifier) => void;
  activeCorrespondentIdSet?: Set<Identifier> | null;
  scrollRef?: RefObject<HTMLElement | null>;
  // Desk specific (optional for now or handled via intersection)
  tenantId?: Identifier | null;
  viewId?: string | null;
  ensureDownloadUrl?: (docId: Identifier, options?: { force?: boolean }) => Promise<any>;
  activeTagFilters?: Array<Identifier | null>;
}

const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = ({
  headerLeading = null,
  onBreadcrumbNavigate,
  currentFolderName,
  breadcrumbs,
  subfolders,
  documents,
  searchResultIds,
  onFolderSelect,
  onFolderDrop,
  onFolderDragOver,
  onFolderDragLeave,
  onFolderDragStart,
  onFolderDragEnd,
  draggedFolderId,
  onFolderRename,
  draggingDocumentIds = [],
  onDocumentDragStart,
  onDocumentDragEnd,
  onDocumentRename,
  onEntryPointer = null,
  onDocumentActivate = null,
  tagLookupById,
  activeCorrespondentIds = [],
  ensureAssetUrl = null,
  getDocumentAsset,
  isSearchLoading = false,
  viewMode = 'list',
  onViewModeChange,
  onRefresh = () => { },
  sortField,
  sortDirection,
  onSortFieldChange,
  onSortDirectionToggle,
  onDeleteSelection,
  documentLookup,
  tags,
  correspondents,
  onBulkTagAdd,
  onBulkTagRemove,
  onBulkCorrespondentAdd,
  onBulkCorrespondentRemove,
  onBulkReanalyze,
  folderOptions,
  onMoveDocumentsToFolder,
  searchQuery = '',
  activeTagFilters = [],
  activeCorrespondentFilters = [],
  selectedFolder = null,
  onDocumentTagAttach,
  onDocumentTagDetach,
  currentTenantId,
}): ReactNode => {
  const {
    setFocusedEntryKey,
    clearSelection,
  } = useWorkspaceSelectionContext();
  const {
    isActive: isFilterActive,
    includeDescendants,
    toggleIncludeDescendants,
    toggleTag: toggleTagFilter,
    toggleCorrespondent: toggleCorrespondentFilter,
  } = useDocumentsFilter();
  const searchDocuments = useMemo(
    () =>
      Array.isArray(searchResultIds)
        ? searchResultIds
          .map((id) => documentLookup?.get?.(id) || null)
          .filter((doc): doc is Record<string, unknown> => Boolean(doc))
        : null,
    [searchResultIds, documentLookup],
  );

  const showingSearchResults = Array.isArray(searchResultIds);
  const rows = showingSearchResults && searchDocuments ? searchDocuments : documents;

  const searchResultCount = Array.isArray(searchResultIds) ? searchResultIds.length : 0;

  const viewId = 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 headerTitle = showingSearchResults
    ? 'Search results'
    : currentFolderName || 'Documents';
  const headerSubtitle = showingSearchResults
    ? `${searchResultCount} matching document${searchResultCount === 1 ? '' : 's'}`
    : null;
  const headerActions = useMemo(
    () => createDocumentsTableHeaderActions({
      viewMode,
      onViewModeChange,
      onRefresh,
      sortField,
      onSortFieldChange,
      sortDirection,
      onSortDirectionToggle,
      isFilterActive,
      includeDescendants,
      onToggleIncludeDescendants: toggleIncludeDescendants,
    }),
    [
      viewMode,
      onViewModeChange,
      onRefresh,
      sortField,
      onSortFieldChange,
      sortDirection,
      onSortDirectionToggle,
      isFilterActive,
      includeDescendants,
      toggleIncludeDescendants,
    ],
  );
  const floatingActions = useMemo(() => (
    <SelectionFloatingPanel
      documentLookup={documentLookup}
      tags={tags}
      tagLookupById={tagLookupById}
      correspondents={correspondents}
      onBulkTagAdd={onBulkTagAdd}
      onBulkTagRemove={onBulkTagRemove}
      onBulkCorrespondentAdd={onBulkCorrespondentAdd}
      onBulkCorrespondentRemove={onBulkCorrespondentRemove}
      onBulkReanalyze={onBulkReanalyze}
      onDeleteSelection={onDeleteSelection}
      folderOptions={folderOptions}
      onMoveDocumentsToFolder={onMoveDocumentsToFolder}
      onClearSelection={clearSelection}
    />
  ), [
    documentLookup,
    tags,
    tagLookupById,
    correspondents,
    onBulkTagAdd,
    onBulkTagRemove,
    onBulkCorrespondentAdd,
    onBulkCorrespondentRemove,
    onBulkReanalyze,
    onDeleteSelection,
    folderOptions,
    onMoveDocumentsToFolder,
    clearSelection,
  ]);
  const headerConfig: DocumentsPanelHeaderConfig = useMemo(() => ({
    title: headerTitle,
    subtitle: headerSubtitle,
    leading: headerLeading,
    actions: headerActions,
    breadcrumbs,
    floatingActions,
  }), [
    headerTitle,
    headerSubtitle,
    headerLeading,
    headerActions,
    breadcrumbs,
    floatingActions,
  ]);

  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: searchResultIds }
      : { 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) {
      clearSelection();
    }
  }, [showingSearchResults, currentFolderId, searchResultIds, clearSelection]);

  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 draggingSet = useMemo(
    () => new Set(draggingDocumentIds || []),
    [draggingDocumentIds],
  );
  const activeCorrespondentIdSet = useMemo(
    () => new Set(activeCorrespondentIds || []),
    [activeCorrespondentIds],
  );
  const scrollRef = useRef<HTMLElement | null>(null);
  const suppressDocumentClickRef = useRef(false);
  const isGridView = viewMode === 'grid';
  const isDeskView = viewMode === 'desk';
  const gridIconSize = DEFAULT_GRID_ICON_SIZE;

  const { openPreview } = usePreviewContext();

  const isTagDragEvent = useCallback((event) => isTagTransferEvent(event), []);

  const draggingTagRef = useRef<{ docId: Identifier; tagId: Identifier } | null>(null);

  const handleDocumentTagDragStart = useCallback(
    (_event, docId, tagId) => {
      draggingTagRef.current = { docId, tagId };
    },
    [],
  );

  const handleDocumentTagDragEnd = useCallback(
    (_event) => {
      draggingTagRef.current = null;
    },
    [],
  );

  const handleDocumentTagDragOver = useCallback(
    (event, docId) => {
      if (!isTagDragEvent(event)) {
        return;
      }
      event.preventDefault();

      const isSource = draggingTagRef.current?.docId === docId;
      event.dataTransfer.dropEffect = isSource ? 'copy' : 'move';

      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 handleDocumentActivate = useCallback(
    (doc, event?: React.MouseEvent | KeyboardEvent | null) => {
      if (!doc) {
        return;
      }

      // Handle Preview (Alt+Click or Middle Click)
      if (event && (event.altKey || ((event as React.MouseEvent).button === 1))) {
        openPreview(doc);
        return;
      }

      // Handle Activation (Double Click, Enter, or explicit call)
      if (onDocumentActivate) {
        onDocumentActivate(doc);
      }
    },
    [onDocumentActivate, openPreview],
  );

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

      if (onEntryPointer) {
        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 });
        setFocusedEntryKey(`folder:${folder.id}`);
      }
    },
    [onEntryPointer, setFocusedEntryKey],
  );

  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 viewProps = {
    entries,
    draggingDocumentIdsSet: draggingSet,
    draggedFolderId,
    onFolderClick: handleFolderClick,
    onFolderSelect,
    onFolderDragOver,
    onFolderDragLeave,
    onFolderDrop,
    onFolderDragStart,
    onFolderDragEnd,
    onFolderRename,
    onDocumentActivate: handleDocumentActivate,
    onDocumentDragStart: handleDocumentDragStartLocal,
    onDocumentDragEnd: handleDocumentDragEndLocal,
    onDocumentTagDragStart: handleDocumentTagDragStart,
    onDocumentTagDragEnd: handleDocumentTagDragEnd,
    onDocumentTagDragOver: handleDocumentTagDragOver,
    onDocumentTagDragLeave: handleDocumentTagDragLeave,
    onDocumentTagAttach,
    onDocumentTagDetach,
    onDocumentRename,
    ensureAssetUrl,
    getDocumentAsset,
    tagLookupById,
    onTagClick: toggleTagFilter,
    scrollRef,
    activeCorrespondentIdSet: activeCorrespondentIdSet,
    onCorrespondentClick: toggleCorrespondentFilter,
    tenantId: currentTenantId,
    viewId,
    onEntryPointer,
  };

  const renderBody = () => {
    const hasEntries = entries.length > 0;
    const isSearchEmpty = (showingSearchResults || isFilterActive) && !hasDocumentEntries && !isSearchLoading;

    if (isSearchEmpty) {
      return (
        <div className="empty-state">
          No documents match the current filters.
        </div>
      );
    }

    if (!hasEntries) {
      return (
        <div className="empty-state">
          No documents to show here yet. Drop files to make this space come alive.
        </div>
      );
    }

    switch (viewMode) {
      case 'desk':
        return <DesktopWorkspace {...viewProps} />;
      case 'grid':
        return <DocumentsGrid {...viewProps} gridIconSize={gridIconSize} />;
      case 'list':
      default:
        return <DocumentsList {...viewProps} />;
    }
  };

  const panelVariant = isDeskView ? 'desk' : isGridView ? 'grid' : 'list';
  const shouldHandlePanelInteractions = !isDeskView && entries.length > 0;

  const handleSectionClick = useCallback((event: React.MouseEvent<HTMLElement>) => {
    if (!shouldHandlePanelInteractions || event.target !== event.currentTarget) {
      return;
    }
    clearSelection();
  }, [shouldHandlePanelInteractions, clearSelection]);

  return (
    <>
      <DocumentsPanelHeader
        header={headerConfig}
        onBreadcrumbClick={onBreadcrumbNavigate}
      />
      <section
        ref={scrollRef}
        className={`documents-panel documents-panel--view-${panelVariant}`}
        onClick={handleSectionClick}
      >
        {renderBody()}
      </section>
    </>
  );
};

const DocumentsPanel: React.FC<DocumentsPanelProps> = ({ selectionValue, ...rest }) => (
  <WorkspaceSelectionProvider value={selectionValue}>
    <DocumentsPanelInner {...rest} />
  </WorkspaceSelectionProvider>
);

export default DocumentsPanel;
