'use client';

import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react';
import { useDocuments } from '@/contexts/DocumentContext';
import type {
  DocumentListDocument,
  DocumentListState,
  Folder,
  IconSize,
  SidebarFilter,
  SortBy,
  SortDirection,
  ViewMode,
} from '@/types/documents';
import {
  getDocumentListState,
  getDocumentRecentlyOpenedMap,
  saveDocumentListState,
} from '@/lib/client/dexie';
import { ConfirmDialog } from '@/components/ConfirmDialog';
import { CreateFolderDialog } from '@/components/doclist/CreateFolderDialog';
import { DocumentListSkeleton } from '@/components/doclist/DocumentListSkeleton';
import { DocumentUploader } from '@/components/documents/DocumentUploader';
import { DocumentDndProvider } from './dnd/DocumentDndProvider';
import {
  DocumentSelectionProvider,
  useDocumentSelection,
} from './dnd/DocumentSelectionContext';
import type { DocumentDragItem } from './dnd/dndTypes';
import { FinderWindow, useIsNarrow } from './window/FinderWindow';
import { FinderToolbar } from './window/FinderToolbar';
import { FinderSidebar } from './window/FinderSidebar';
import { FinderStatusBar } from './window/FinderStatusBar';
import { IconsView } from './views/IconsView';
import { ListView } from './views/ListView';
import { ColumnsView } from './views/ColumnsView';
import { GalleryView } from './views/GalleryView';

type DocumentToDelete = {
  id: string;
  name: string;
  type: DocumentListDocument['type'];
};

const DEFAULT_STATE: Required<
  Pick<
    DocumentListState,
    'sortBy' | 'sortDirection' | 'folders' | 'collapsedFolders' | 'showHint'
  >
> & {
  viewMode: ViewMode;
  iconSize: IconSize;
  sidebarWidth: number;
  sidebarFilter: SidebarFilter;
  sidebarCollapsed: boolean;
} = {
  sortBy: 'name',
  sortDirection: 'asc',
  folders: [],
  collapsedFolders: [],
  showHint: true,
  viewMode: 'icons',
  iconSize: 'md',
  sidebarWidth: 220,
  sidebarFilter: 'all',
  sidebarCollapsed: false,
};

function normalizeViewMode(stored: DocumentListState['viewMode']): ViewMode {
  if (stored === 'grid' || stored === undefined) return 'icons';
  if (stored === 'list') return 'list';
  return stored;
}

function generateDefaultFolderName(
  doc1: DocumentListDocument,
  doc2: DocumentListDocument,
): string {
  const words1 = doc1.name.toLowerCase().split(/[\s\-_.]+/);
  const words2 = doc2.name.toLowerCase().split(/[\s\-_.]+/);
  const common = words1.filter((w) => words2.includes(w));
  const significant = common.find((w) => w.length >= 3);
  if (significant) {
    if (significant === 'pdf') return 'PDFs';
    if (significant === 'epub') return 'EPUBs';
    if (significant === 'txt' || significant === 'md') return 'Documents';
    return significant.charAt(0).toUpperCase() + significant.slice(1);
  }
  const timestamp = new Date().toISOString().slice(0, 10);
  return `Folder ${timestamp}`;
}

function sortDocs(
  docs: DocumentListDocument[],
  sortBy: SortBy,
  direction: SortDirection,
): DocumentListDocument[] {
  const sorted = [...docs].sort((a, b) => {
    switch (sortBy) {
      case 'name':
        return a.name.localeCompare(b.name);
      case 'type':
        return a.type.localeCompare(b.type);
      case 'size':
        return a.size - b.size;
      default:
        return a.lastModified - b.lastModified;
    }
  });
  return direction === 'asc' ? sorted : sorted.reverse();
}

interface DocumentListInnerProps {
  brand?: ReactNode;
  appActions?: ReactNode;
}

function DocumentListInner({ brand, appActions }: DocumentListInnerProps) {
  const [sortBy, setSortBy] = useState<SortBy>(DEFAULT_STATE.sortBy);
  const [sortDirection, setSortDirection] = useState<SortDirection>(
    DEFAULT_STATE.sortDirection,
  );
  const [viewMode, setViewMode] = useState<ViewMode>(DEFAULT_STATE.viewMode);
  const [iconSize, setIconSize] = useState<IconSize>(DEFAULT_STATE.iconSize);
  const [folders, setFolders] = useState<Folder[]>(DEFAULT_STATE.folders);
  const [showHint, setShowHint] = useState(true);
  const [sidebarWidth, setSidebarWidth] = useState(DEFAULT_STATE.sidebarWidth);
  const [sidebarFilter, setSidebarFilter] = useState<SidebarFilter>('all');
  const [sidebarOpen, setSidebarOpen] = useState(true);
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
  const [query, setQuery] = useState('');
  const [recentlyOpenedById, setRecentlyOpenedById] = useState<Record<string, number>>({});

  const [isInitialized, setIsInitialized] = useState(false);

  const [documentToDelete, setDocumentToDelete] = useState<DocumentToDelete | null>(null);
  const [pendingMerge, setPendingMerge] = useState<
    | { sources: DocumentListDocument[]; target: DocumentListDocument }
    | null
  >(null);
  const [newFolderName, setNewFolderName] = useState('');
  const [manualFolderPrompt, setManualFolderPrompt] = useState(false);

  const isNarrow = useIsNarrow();
  const selection = useDocumentSelection();

  const {
    pdfDocs,
    removePDFDocument,
    isPDFLoading,
    epubDocs,
    removeEPUBDocument,
    isEPUBLoading,
    htmlDocs,
    removeHTMLDocument,
    isHTMLLoading,
  } = useDocuments();

  // Load saved state.
  useEffect(() => {
    let cancelled = false;
    (async () => {
      const saved = await getDocumentListState();
      if (cancelled) return;
      if (saved) {
        setSortBy(saved.sortBy);
        setSortDirection(saved.sortDirection);
        setFolders(saved.folders ?? []);
        setShowHint(saved.showHint ?? true);
        setViewMode(normalizeViewMode(saved.viewMode));
        setIconSize(saved.iconSize ?? DEFAULT_STATE.iconSize);
        setSidebarWidth(saved.sidebarWidth ?? DEFAULT_STATE.sidebarWidth);
        setSidebarFilter(saved.sidebarFilter ?? 'all');
        setSidebarOpen(!(saved.sidebarCollapsed ?? false));
      }
      setIsInitialized(true);
    })();
    return () => {
      cancelled = true;
    };
  }, []);

  // Persist.
  useEffect(() => {
    if (!isInitialized) return;
    const state: DocumentListState = {
      sortBy,
      sortDirection,
      folders,
      collapsedFolders: [],
      showHint,
      viewMode,
      iconSize,
      sidebarWidth,
      sidebarFilter,
      sidebarCollapsed: !sidebarOpen,
    };
    void saveDocumentListState(state);
  }, [
    sortBy,
    sortDirection,
    folders,
    showHint,
    viewMode,
    iconSize,
    sidebarWidth,
    sidebarFilter,
    sidebarOpen,
    isInitialized,
  ]);

  // Mobile drawer should never auto-open from persisted desktop state.
  useEffect(() => {
    if (!isNarrow) return;
    setMobileSidebarOpen(false);
  }, [isNarrow]);

  // Build the union document list.
  const rawDocuments: DocumentListDocument[] = useMemo(
    () => [
      ...pdfDocs.map((d) => ({ ...d, type: 'pdf' as const })),
      ...epubDocs.map((d) => ({ ...d, type: 'epub' as const })),
      ...htmlDocs.map((d) => ({ ...d, type: 'html' as const })),
    ],
    [pdfDocs, epubDocs, htmlDocs],
  );
  const rawDocumentIdsKey = useMemo(
    () => rawDocuments.map((d) => d.id).sort().join('|'),
    [rawDocuments],
  );

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const recentMap = await getDocumentRecentlyOpenedMap();
        if (cancelled) return;
        setRecentlyOpenedById(recentMap);
      } catch (err) {
        console.warn('Failed to load recently opened cache metadata:', err);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [rawDocumentIdsKey]);

  const allDocuments: DocumentListDocument[] = useMemo(
    () =>
      rawDocuments.map((doc) => ({
        ...doc,
        recentlyOpenedAt: recentlyOpenedById[doc.id] ?? 0,
      })),
    [rawDocuments, recentlyOpenedById],
  );

  const allDocumentsById = useMemo(() => {
    const map = new Map<string, DocumentListDocument>();
    for (const doc of allDocuments) map.set(doc.id, doc);
    return map;
  }, [allDocuments]);

  const foldersWithLiveDocs = useMemo(
    () =>
      folders.map((folder) => ({
        ...folder,
        documents: folder.documents
          .map((d) => allDocumentsById.get(d.id))
          .filter((d): d is DocumentListDocument => Boolean(d))
          .map((d) => ({ ...d, folderId: folder.id })),
      })),
    [folders, allDocumentsById],
  );

  const folderIdByDocId = useMemo(() => {
    const map = new Map<string, string>();
    for (const folder of foldersWithLiveDocs) {
      for (const doc of folder.documents) map.set(doc.id, folder.id);
    }
    return map;
  }, [foldersWithLiveDocs]);

  const allDocumentsWithFolder = useMemo(
    () =>
      allDocuments.map((doc) => ({
        ...doc,
        folderId: folderIdByDocId.get(doc.id),
      })),
    [allDocuments, folderIdByDocId],
  );

  // Filter based on sidebar selection + search query.
  const visibleDocuments = useMemo(() => {
    const q = query.trim().toLowerCase();
    let docs = allDocumentsWithFolder;
    if (sidebarFilter === 'pdf') docs = docs.filter((d) => d.type === 'pdf');
    else if (sidebarFilter === 'epub') docs = docs.filter((d) => d.type === 'epub');
    else if (sidebarFilter === 'html') docs = docs.filter((d) => d.type === 'html');
    else if (sidebarFilter === 'recents') {
      docs = [...docs]
        .filter((d) => (d.recentlyOpenedAt ?? 0) > 0)
        .sort((a, b) => (b.recentlyOpenedAt ?? 0) - (a.recentlyOpenedAt ?? 0))
        .slice(0, 20);
    } else if (sidebarFilter.startsWith('folder:')) {
      const fid = sidebarFilter.slice('folder:'.length);
      const folder = foldersWithLiveDocs.find((f) => f.id === fid);
      docs = folder
        ? folder.documents
            .map((d) => allDocumentsById.get(d.id))
            .filter((d): d is DocumentListDocument => Boolean(d))
            .map((d) => ({ ...d, folderId: fid }))
        : [];
    }
    if (q) docs = docs.filter((d) => d.name.toLowerCase().includes(q));
    return docs;
  }, [allDocumentsWithFolder, sidebarFilter, query, foldersWithLiveDocs, allDocumentsById]);

  // Apply sort.
  const sortedVisible = useMemo(() => {
    if (sidebarFilter === 'recents') return visibleDocuments;
    return sortDocs(visibleDocuments, sortBy, sortDirection);
  }, [visibleDocuments, sidebarFilter, sortBy, sortDirection]);

  const counts = useMemo(
    () => ({
      all: allDocuments.length,
      pdf: pdfDocs.length,
      epub: epubDocs.length,
      html: htmlDocs.length,
    }),
    [allDocuments.length, pdfDocs.length, epubDocs.length, htmlDocs.length],
  );

  // --- Actions ---

  const handleDelete = useCallback(async () => {
    if (!documentToDelete) return;
    try {
      if (documentToDelete.type === 'pdf') await removePDFDocument(documentToDelete.id);
      else if (documentToDelete.type === 'epub') await removeEPUBDocument(documentToDelete.id);
      else if (documentToDelete.type === 'html') await removeHTMLDocument(documentToDelete.id);
      setFolders((prev) =>
        prev.map((f) => ({
          ...f,
          documents: f.documents.filter(
            (d) => !(d.id === documentToDelete.id && d.type === documentToDelete.type),
          ),
        })),
      );
      setDocumentToDelete(null);
    } catch (err) {
      console.error('Failed to remove document:', err);
    }
  }, [documentToDelete, removePDFDocument, removeEPUBDocument, removeHTMLDocument]);

  const handleDeleteDoc = useCallback((doc: DocumentListDocument) => {
    setDocumentToDelete({ id: doc.id, name: doc.name, type: doc.type });
  }, []);

  const handleDropOnFolder = useCallback(
    (folderId: string, item: DocumentDragItem) => {
      setFolders((prev) =>
        prev.map((f) => {
          if (f.id !== folderId) {
            // Remove the dropped docs from any other folder they were in.
            return {
              ...f,
              documents: f.documents.filter(
                (d) => !item.ids.includes(d.id),
              ),
            };
          }
          const existingIds = new Set(f.documents.map((d) => d.id));
          const newDocs = item.docs
            .filter((d) => !existingIds.has(d.id))
            .map((d) => ({ ...d, folderId }));
          return { ...f, documents: [...f.documents, ...newDocs] };
        }),
      );
      setSidebarFilter(`folder:${folderId}`);
      selection.clear();
    },
    [selection],
  );

  const handleMergeIntoFolder = useCallback(
    (sources: DocumentListDocument[], target: DocumentListDocument) => {
      if (target.folderId) return;
      const filtered = sources.filter((s) => s.id !== target.id && !s.folderId);
      if (filtered.length === 0) return;
      setPendingMerge({ sources: filtered, target });
      setNewFolderName('');
    },
    [],
  );

  const createFolderFromPending = useCallback(() => {
    if (!pendingMerge) return;
    const name =
      newFolderName.trim() ||
      generateDefaultFolderName(pendingMerge.sources[0], pendingMerge.target);
    const folderId = `folder-${Date.now()}`;
    setFolders((prev) => [
      ...prev,
      {
        id: folderId,
        name,
        documents: [
          ...pendingMerge.sources.map((d) => ({ ...d, folderId })),
          { ...pendingMerge.target, folderId },
        ],
      },
    ]);
    setPendingMerge(null);
    setNewFolderName('');
    setShowHint(false);
    setSidebarFilter(`folder:${folderId}`);
    selection.clear();
  }, [pendingMerge, newFolderName, selection]);

  const createManualFolder = useCallback(() => {
    const name = newFolderName.trim() || `New Folder`;
    const folderId = `folder-${Date.now()}`;
    setFolders((prev) => [...prev, { id: folderId, name, documents: [] }]);
    setNewFolderName('');
    setManualFolderPrompt(false);
    setSidebarFilter(`folder:${folderId}`);
  }, [newFolderName]);

  const handleDeleteFolder = useCallback((folderId: string) => {
    setFolders((prev) => prev.filter((f) => f.id !== folderId));
    if (sidebarFilter === `folder:${folderId}`) setSidebarFilter('all');
  }, [sidebarFilter]);

  // Status bar summary.
  const summary = useMemo(() => {
    const parts: string[] = [];
    if (counts.pdf) parts.push(`${counts.pdf} PDF${counts.pdf === 1 ? '' : 's'}`);
    if (counts.epub) parts.push(`${counts.epub} EPUB${counts.epub === 1 ? '' : 's'}`);
    if (counts.html) parts.push(`${counts.html} HTML${counts.html === 1 ? '' : 's'}`);
    return parts.join(' • ');
  }, [counts]);

  const totalBytes = useMemo(
    () => allDocuments.reduce((acc, d) => acc + d.size, 0),
    [allDocuments],
  );

  const isLoading = isPDFLoading || isEPUBLoading || isHTMLLoading;

  const fallbackViewMode: ViewMode =
    viewMode === 'columns' && isNarrow ? 'list' : viewMode;
  const effectiveSidebarOpen = isNarrow ? mobileSidebarOpen : sidebarOpen;

  return (
    <FinderWindow
      toolbar={
        <FinderToolbar
          viewMode={fallbackViewMode}
          onViewModeChange={setViewMode}
          iconSize={iconSize}
          onIconSizeChange={setIconSize}
          sortBy={sortBy}
          sortDirection={sortDirection}
          onSortByChange={setSortBy}
          onSortDirectionToggle={() =>
            setSortDirection((p) => (p === 'asc' ? 'desc' : 'asc'))
          }
          query={query}
          onQueryChange={setQuery}
          onToggleSidebar={() =>
            isNarrow
              ? setMobileSidebarOpen((p) => !p)
              : setSidebarOpen((p) => !p)
          }
          isSidebarOpen={effectiveSidebarOpen}
          isNarrow={isNarrow}
          leftSlot={brand}
          rightSlot={appActions}
        />
      }
      sidebar={
        <FinderSidebar
          filter={sidebarFilter}
          onFilterChange={setSidebarFilter}
          folders={foldersWithLiveDocs}
          counts={counts}
          onDeleteFolder={handleDeleteFolder}
          onNewFolder={() => {
            setNewFolderName('');
            setManualFolderPrompt(true);
          }}
          onDropOnFolder={handleDropOnFolder}
          width={sidebarWidth}
          onWidthChange={setSidebarWidth}
          topSlot={<DocumentUploader variant="compact" />}
        />
      }
      statusBar={
        <FinderStatusBar
          itemCount={allDocuments.length}
          selectedCount={selection.selectionSize}
          totalSize={totalBytes}
          summary={summary}
        />
      }
      sidebarOpen={effectiveSidebarOpen}
      onSidebarOpenChange={(open) => {
        if (isNarrow) setMobileSidebarOpen(open);
        else setSidebarOpen(open);
      }}
    >
      {!isLoading && showHint && allDocuments.length > 1 && (
        <div className="px-3 pt-3 shrink-0 bg-background">
          <div className="flex items-center justify-between bg-base border border-offbase rounded-md px-3 py-1 text-[12px]">
            <p className="text-foreground">
              Drag files onto each other to make folders. Drop into the sidebar to move.
            </p>
            <button
              type="button"
              onClick={() => setShowHint(false)}
              className="h-6 w-6 inline-flex items-center justify-center text-muted hover:text-accent hover:bg-base hover:scale-[1.01] rounded transition-all duration-200 ease-out"
              aria-label="Dismiss hint"
            >
              <svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
              </svg>
            </button>
          </div>
        </div>
      )}

      {isLoading ? (
        <div className="flex-1 min-h-0 overflow-hidden">
          <DocumentListSkeleton viewMode={fallbackViewMode} iconSize={iconSize} />
        </div>
      ) : allDocuments.length === 0 ? (
        <div className="flex-1 min-h-0 flex items-center justify-center p-6">
          <DocumentUploader className="py-12 w-full max-w-2xl" />
        </div>
      ) : (
        <DocumentUploader variant="overlay" className="flex-1 min-h-0 flex flex-col">
          {fallbackViewMode === 'icons' && (
            <IconsView
              documents={sortedVisible}
              iconSize={iconSize}
              onDeleteDoc={handleDeleteDoc}
              onMergeIntoFolder={handleMergeIntoFolder}
            />
          )}
          {fallbackViewMode === 'list' && (
            <ListView
              documents={sortedVisible}
              sortBy={sortBy}
              sortDirection={sortDirection}
              onSortChange={(b, d) => {
                setSortBy(b);
                setSortDirection(d);
              }}
              onDeleteDoc={handleDeleteDoc}
              onMergeIntoFolder={handleMergeIntoFolder}
            />
          )}
          {fallbackViewMode === 'columns' && (
            <ColumnsView
              documents={sortedVisible}
              onDeleteDoc={handleDeleteDoc}
              onMergeIntoFolder={handleMergeIntoFolder}
            />
          )}
          {fallbackViewMode === 'gallery' && (
            <GalleryView
              documents={sortedVisible}
              onDeleteDoc={handleDeleteDoc}
              onMergeIntoFolder={handleMergeIntoFolder}
            />
          )}
        </DocumentUploader>
      )}

      <CreateFolderDialog
        isOpen={pendingMerge !== null}
        onClose={() => setPendingMerge(null)}
        folderName={newFolderName}
        onFolderNameChange={setNewFolderName}
        onKeyDown={(e) => {
          if (e.key === 'Enter') {
            e.preventDefault();
            createFolderFromPending();
          } else if (e.key === 'Escape') {
            setPendingMerge(null);
            setNewFolderName('');
          }
        }}
      />

      <CreateFolderDialog
        isOpen={manualFolderPrompt}
        onClose={() => setManualFolderPrompt(false)}
        folderName={newFolderName}
        onFolderNameChange={setNewFolderName}
        onKeyDown={(e) => {
          if (e.key === 'Enter') {
            e.preventDefault();
            createManualFolder();
          } else if (e.key === 'Escape') {
            setManualFolderPrompt(false);
            setNewFolderName('');
          }
        }}
      />

      <ConfirmDialog
        isOpen={documentToDelete !== null}
        onClose={() => setDocumentToDelete(null)}
        onConfirm={handleDelete}
        title="Delete Document"
        message={`Are you sure you want to delete ${documentToDelete?.name ?? 'this document'}?`}
        confirmText="Delete"
        isDangerous
      />
    </FinderWindow>
  );
}

export function DocumentList({
  brand,
  appActions,
}: {
  brand?: ReactNode;
  appActions?: ReactNode;
} = {}) {
  return (
    <DocumentDndProvider>
      <DocumentSelectionProvider>
        <DocumentListInner brand={brand} appActions={appActions} />
      </DocumentSelectionProvider>
    </DocumentDndProvider>
  );
}
