"use client";

import { useEffect, useRef, useState } from "react";
import Link from "next/link";
import {
  Archive, ArrowUpRight, CalendarRange, ChevronDown, ChevronLeft, ChevronRight, Clock, CloudUpload, Database, Download, FileQuestion, Files,
  FolderInput, FolderPlus, LayoutGrid, Link2, List, PanelLeft, RefreshCw, SlidersHorizontal, Tag, Upload, X,
} from "lucide-react";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge";
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Dialog, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Progress } from "@/components/ui/progress";
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { Skeleton } from "@/components/ui/skeleton";
import { Spinner } from "@/components/ui/spinner";
import { Switch } from "@/components/ui/switch";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { DateRangeFilter, FilterCombobox, FilterSelect, SearchInput } from "@/components/app/filters";
import { PageHeader } from "@/components/app/page";
import { cn } from "@/lib/utils";
import { EmptyState, ErrorState } from "../components/ui/page-states";
import { useTeamMemberOptions } from "../lib/useTeamMemberOptions";
import { formatDate } from "../lib/format";
import { CategoryDialog, DocumentEditDialog, DocumentSupersedeDialog, MoveFilesDialog, UploadDialog } from "./file-dialogs";
import { FileDetails, Lightbox } from "./file-details";
import { OWNER_TYPE_OPTIONS } from "./file-nodes";
import { FileTree } from "./file-tree";
import { formatBytes } from "./file-utils";
import { FileGrid, FileListSkeleton, FileTable } from "./file-views";
import { FolderNameDialog } from "./folder-components";
import { useArchiveFiles, useElementWidth } from "./useFileManager";
import { useFileExplorer } from "./useFileExplorer";
import { documentDownloadUrl } from "./useDocuments";
import { filesZipUrl, useStorageSummary } from "./useFiles";

// Container widths (px) where the layout changes; the explorer runs on /documents, in a full-screen
// dialog and inside record pages, so it adapts to its own width rather than the viewport.
const WIDE = 1060; // tree + list + details column (the details column shows once a file is selected)
const MEDIUM = 760; // tree + list; details open in a sheet
const SIDEBAR = 264;
const DETAILS = 360;
const ZIP_LIMIT = 50;
const ZIP_MAX_BYTES = 200 * 1024 * 1024;

/**
 * The file explorer: a tree of buyers › inquiries › styles / purchase orders › shipments …, the files of the
 * selected place, and the details of the selected file.
 * variant: "page" (/documents), "modal" (full-screen dialog; onClose), "inline" (record page section).
 * rootNode / initialNode: top of the tree and where to open. lockRoot keeps an inline view inside its record.
 * url: useUrlFilters(EXPLORER_KEYS) result so the page keeps its place in the address bar.
 * headerActions: extra controls in the inline/modal header (e.g. "Open full screen", "Include PO files").
 */
export function FileExplorer({ rootNode = "root", initialNode = "", variant = "page", lockRoot = false, defaultRecursive = false, url = null, title, headerActions = null, onClose, onChanged, refreshSignal = 0 }) {
  const [shellRef, width] = useElementWidth();
  const measured = width || (variant === "inline" ? MEDIUM : WIDE);
  const layout = measured >= WIDE ? "wide" : measured >= MEDIUM ? "medium" : "narrow";
  const ex = useFileExplorer({ rootNode, initialNode, lockRoot, defaultRecursive, url, compact: layout === "narrow" });
  // A record with nothing filed under it (e.g. a shipment) needs no folder column.
  const rootChildren = ex.tree.children[rootNode];
  const showSidebar = layout !== "narrow" && !(lockRoot && rootChildren && !rootChildren.isLoading && !rootChildren.items?.length);
  const storage = useStorageSummary(ex.list.storage, variant !== "inline");
  const [navOpen, setNavOpen] = useState(false);
  const [detailsOpen, setDetailsOpen] = useState(false);
  const [historySignal, setHistorySignal] = useState(0);
  const [dialog, setDialog] = useState(null); // { type, documents?, files?, item? }
  const [lightboxOpen, setLightboxOpen] = useState(false);
  const [dragging, setDragging] = useState(false);
  const isModal = variant === "modal";
  const isPage = variant === "page";
  const detailsDocked = layout === "wide" && Boolean(ex.activeDocument);
  const mainWidth = measured - (showSidebar ? SIDEBAR : 0) - (detailsDocked ? DETAILS : 0);
  // Optional columns only when files are listed and there is room; the name column keeps at least ~260px.
  const hasFiles = ex.documents.length > 0;
  const columns = { size: hasFiles && mainWidth >= 600, type: hasFiles && mainWidth >= 900, user: hasFiles && mainWidth >= 800, path: hasFiles && mainWidth >= 720 };
  // "Filed in" only matters when the list mixes records.
  const showPath = (ex.everywhere || ex.recursive || Boolean(ex.special) || Boolean(ex.state.q) || ex.isContainer) && ex.documents.some((document) => document.owner?.node && document.owner.node !== ex.node);
  const { refreshAll } = ex;

  // Refresh once per signal change. refreshAll is a new function on every render, so it must not be a dependency:
  // with it, a signal that stays set (after closing the full-screen explorer once) refetched on every render.
  const refreshRef = useRef(refreshAll);
  refreshRef.current = refreshAll;
  const handledSignal = useRef(refreshSignal);
  useEffect(() => {
    if (!refreshSignal || handledSignal.current === refreshSignal) return;
    handledSignal.current = refreshSignal;
    refreshRef.current();
  }, [refreshSignal]);

  const changed = () => { refreshAll(); onChanged?.(); };
  const { archiveFiles } = useArchiveFiles(ex.actions.archive, () => { ex.selection.clear(); onChanged?.(); });

  const openDocument = (document, action = "") => {
    if (action === "move") return setDialog({ type: "move", documents: [document] });
    if (action === "edit") return setDialog({ type: "edit", documents: [document] });
    if (action === "category") return setDialog({ type: "category", documents: [document] });
    if (action === "supersede") return setDialog({ type: "supersede", documents: [document] });
    if (action === "archive") return archiveFiles([document]);
    if (action === "newer") return ex.openNewest(document);
    ex.setActiveId(document.id);
    setDetailsOpen(true);
    setHistorySignal((value) => (action === "history" ? value + 1 : 0));
    return undefined;
  };

  const openFolder = (item) => {
    ex.navigate(item.node, item.folder || "");
    setNavOpen(false);
  };
  const onFolderAction = (action, item) => {
    if (action === "open") return openFolder(item);
    if (action === "rename") return setDialog({ type: "rename", item });
    if (action === "delete") return ex.deleteFolder(item);
    return undefined;
  };

  const openUpload = (files) => setDialog({ type: "upload", files: files ? Array.from(files) : [] });
  const targetLabel = ex.targetLabel;

  const uploadButton = <ExplainedButton reason={ex.uploadable ? null : ex.uploadHint} size="sm" onClick={() => openUpload()}>
    <Upload /><span className={cn(measured < 480 && !isPage && "sr-only")}>Upload</span>
  </ExplainedButton>;
  const newFolderButton = ex.canCreateFolder
    ? <Button variant="outline" size="sm" onClick={() => setDialog({ type: "folder" })}><FolderPlus /><span className={cn(mainWidth < 620 && "sr-only")}>New folder</span></Button>
    : ex.uploadable && <ExplainedButton variant="outline" size="sm" reason="Folders can be added inside buyer, factory, inquiry, style and PO folders.">
      <FolderPlus /><span className={cn(mainWidth < 620 && "sr-only")}>New folder</span>
    </ExplainedButton>;

  const detailsPanel = <FileDetails
    document={ex.activeDocument}
    onAction={(action) => ex.activeDocument && openDocument(ex.activeDocument, action)}
    onClose={() => (layout === "wide" ? ex.setActiveId(null) : setDetailsOpen(false))}
    onZoom={() => setLightboxOpen(true)}
    onNavigate={(node, folder) => { ex.navigate(node, folder || ""); setDetailsOpen(false); }}
    scrollToHistory={historySignal}
  />;

  const nav = <ExplorerNav ex={ex} storage={storage} showQuick={!lockRoot} onNavigate={() => setNavOpen(false)} />;
  const Title = isModal ? DialogTitle : "h2";
  const Description = isModal ? DialogDescription : "p";
  const inlineCount = ex.info?.counts ? (ex.info.counts.rollup ?? ex.info.counts.own) : null;

  const shell = <div ref={shellRef} className="flex size-full min-h-0 min-w-0 flex-col bg-background">
    {!isPage && <header className="flex flex-wrap items-center gap-x-4 gap-y-2 border-b px-4 py-3">
      <div className="grid min-w-0 flex-1 gap-0.5">
        <Title className="text-sm font-semibold tracking-tight">{variant === "inline" ? (typeof inlineCount === "number" ? `${inlineCount} ${inlineCount === 1 ? "file" : "files"}` : title || "Files") : title || "Files"}</Title>
        <Description className="truncate text-xs text-muted-foreground">{variant === "inline" ? rollupHint(ex) : <>Every file, filed under its buyer, order and shipment. Opened at <strong className="font-medium text-foreground">{targetLabel}</strong>.</>}</Description>
      </div>
      <div className="flex items-center gap-2">
        {isModal && layout !== "narrow" && <StorageMeter storage={storage} compact />}
        {headerActions}
        {isModal && <Button variant="ghost" size="icon" onClick={onClose} aria-label="Close files"><X /></Button>}
      </div>
    </header>}

    <div className="flex min-h-0 flex-1">
      {showSidebar && <aside className="flex shrink-0 flex-col border-r bg-muted/20" style={{ width: SIDEBAR }} aria-label="Browse files">{nav}</aside>}

      <main
        className="relative flex min-w-0 flex-1 flex-col"
        onDragEnter={(event) => { if (layout !== "narrow" && Array.from(event.dataTransfer?.types || []).includes("Files")) { event.preventDefault(); setDragging(true); } }}
      >
        <Toolbar ex={ex} layout={layout} mainWidth={mainWidth} onBrowse={() => setNavOpen(true)} uploadButton={!isPage ? uploadButton : null} newFolderButton={newFolderButton} />

        <div className="min-h-0 flex-1 overflow-y-auto">
          <div className="grid gap-4 p-4">
            <LocationBar ex={ex} narrow={layout === "narrow"} />
            {ex.special === "archived" && <Alert><Archive /><AlertDescription>Archived files keep their history and links for audit, but cannot be downloaded and do not count toward storage.</AlertDescription></Alert>}

            {ex.selectedDocuments.length > 0 && <BulkBar
              documents={ex.selectedDocuments}
              pending={ex.actions.pending}
              onMove={() => setDialog({ type: "move", documents: ex.selectedDocuments })}
              onCategory={() => setDialog({ type: "category", documents: ex.selectedDocuments })}
              onArchive={() => archiveFiles(ex.selectedDocuments)}
              onClear={ex.selection.clear}
            />}

            <Content ex={ex} layout={layout} columns={columns} showPath={showPath} onOpen={openDocument} onOpenFolder={openFolder} onFolderAction={onFolderAction} onUpload={() => openUpload()} onNewFolder={ex.canCreateFolder ? () => setDialog({ type: "folder" }) : null} />

            <LinkedHere ex={ex} columns={columns} onOpen={openDocument} />
          </div>
        </div>

        {dragging && <DropOverlay
          uploadable={ex.uploadable}
          target={`${targetLabel}${ex.folder ? ` › ${ex.folder.split("/").join(" › ")}` : ""}`}
          onLeave={() => setDragging(false)}
          onDrop={(files) => { setDragging(false); if (ex.uploadable && files.length) openUpload(files); }}
        />}
      </main>

      {detailsDocked && <aside className="shrink-0 border-l" style={{ width: DETAILS }} aria-label="File details">
        <div className="h-full overflow-y-auto">{detailsPanel}</div>
      </aside>}
    </div>

    {layout !== "wide" && <Sheet open={detailsOpen && Boolean(ex.activeDocument)} onOpenChange={setDetailsOpen}>
      <SheetContent side="right" showCloseButton={false} className="w-full gap-0 p-0 sm:max-w-md">
        <SheetHeader className="sr-only"><SheetTitle>File details</SheetTitle><SheetDescription>Preview, where it is filed, versions, and actions for the selected file.</SheetDescription></SheetHeader>
        <div className="h-full overflow-y-auto">{detailsPanel}</div>
      </SheetContent>
    </Sheet>}

    {layout === "narrow" && <Sheet open={navOpen} onOpenChange={setNavOpen}>
      <SheetContent side="left" className="w-[88%] gap-0 p-0 sm:max-w-xs">
        <SheetHeader className="border-b"><SheetTitle>Browse</SheetTitle><SheetDescription>Buyers, orders, shipments and other folders.</SheetDescription></SheetHeader>
        {nav}
      </SheetContent>
    </Sheet>}

    {dialog?.type === "upload" && <UploadDialog
      node={ex.info?.node || ex.node}
      folder={ex.folder}
      targetLabel={targetLabel}
      category={ex.info?.defaultCategory || ""}
      initialFiles={dialog.files}
      onUploaded={(saved) => { if (saved.length) changed(); }}
      onClose={() => setDialog(null)}
    />}
    {dialog?.type === "folder" && <FolderNameDialog mode="create" parent={ex.currentFolder} ownerLabel={targetLabel} folders={ex.folderApi} onClose={() => setDialog(null)} />}
    {dialog?.type === "rename" && <FolderNameDialog mode="rename" folder={{ id: dialog.item.manual?.id, name: dialog.item.label }} ownerLabel={targetLabel} folders={ex.folderApi} onClose={() => setDialog(null)} />}
    {dialog?.type === "move" && <MoveFilesDialog documents={dialog.documents} move={ex.actions.move} initialNode={ex.node} onDone={() => { setDialog(null); ex.selection.clear(); onChanged?.(); }} onClose={() => setDialog(null)} />}
    {dialog?.type === "category" && <CategoryDialog documents={dialog.documents} categorize={ex.actions.categorize} onDone={() => { setDialog(null); onChanged?.(); }} onClose={() => setDialog(null)} />}
    {dialog?.type === "edit" && <DocumentEditDialog document={dialog.documents[0]} documentTypes={ex.categories} onClose={() => setDialog(null)} onSaved={() => { setDialog(null); changed(); }} />}
    {dialog?.type === "supersede" && <DocumentSupersedeDialog
      document={dialog.documents[0]}
      onClose={() => setDialog(null)}
      // A new version comes back as a new document id, so follow it in the details panel.
      onSaved={(saved) => { setDialog(null); if (saved?.id) ex.setActiveId(saved.id); changed(); }}
    />}
    {lightboxOpen && ex.activeDocument && <Lightbox document={ex.activeDocument} onClose={() => setLightboxOpen(false)} />}
  </div>;

  if (isModal) {
    return <Dialog open onOpenChange={(open) => { if (!open) onClose?.(); }}>
      <DialogContent
        showCloseButton={false}
        className="flex h-dvh max-h-dvh w-screen max-w-none flex-col gap-0 overflow-hidden rounded-none p-0 sm:h-[calc(100dvh-2rem)] sm:w-[calc(100vw-2rem)] sm:max-w-[calc(100vw-2rem)] sm:rounded-xl"
      >
        {shell}
      </DialogContent>
    </Dialog>;
  }

  const card = <div className={cn(
    "flex min-w-0 overflow-hidden rounded-xl border bg-card shadow-xs",
    variant === "inline" ? "h-[620px] max-md:h-[680px]" : "h-[calc(100dvh-13rem)] min-h-[560px]",
  )}>{shell}</div>;

  if (!isPage) return card;

  return <>
    <PageHeader
      title="Files"
      description="Every file, filed under its buyer, order and shipment. Drop files into any record folder to upload."
      actions={<>
        <span className="hidden md:inline-flex"><StorageMeter storage={storage} compact /></span>
        {uploadButton}
      </>}
    />
    {card}
  </>;
}

// "Includes 12 files from shipments and inspections" for inline record sections.
function rollupHint(ex) {
  const sections = (ex.tree.children[ex.rootNode]?.items || []).filter((item) => !item.folder && !item.isShortcut && item.count > 0);
  if (!sections.length) return "Files of this record, its folders, and the records filed under it.";
  const parts = sections.map((item) => `${item.count} in ${item.label.toLowerCase()}`);
  return `Includes ${parts.length > 1 ? `${parts.slice(0, -1).join(", ")} and ${parts[parts.length - 1]}` : parts[0]}.`;
}

// A button that stays focusable when disabled so its tooltip can explain why.
function ExplainedButton({ reason, children, ...props }) {
  if (!reason) return <Button {...props}>{children}</Button>;
  return <Tooltip>
    <TooltipTrigger asChild><span tabIndex={0} className="inline-flex"><Button {...props} disabled className={cn(props.className, "pointer-events-none")}>{children}</Button></span></TooltipTrigger>
    <TooltipContent className="max-w-64">{reason}</TooltipContent>
  </Tooltip>;
}

/* ---------- Sidebar ---------- */

function ExplorerNav({ ex, storage, showQuick, onNavigate }) {
  const inTree = !ex.special;
  return <>
    <div className="min-h-0 flex-1 overflow-x-hidden overflow-y-auto">
      <nav className="grid min-w-0 grid-cols-1 gap-4 p-3" aria-label="File places">
        {showQuick && <div className="grid gap-0.5">
          {ex.rootNode === "root" && <NavItem icon={Files} label="All files" active={inTree && ex.node === "root" && !ex.everywhere} onClick={() => { ex.navigate("root"); onNavigate?.(); }} />}
          <NavItem icon={Clock} label="Recent" hint="Files you uploaded or downloaded in the last 30 days" active={ex.special === "recent"} onClick={() => { ex.openSpecial("recent"); onNavigate?.(); }} />
          <NavItem icon={Archive} label="Archived" hint="Hidden from active lists, kept for audit" active={ex.special === "archived"} onClick={() => { ex.openSpecial("archived"); onNavigate?.(); }} />
        </div>}

        <div className="grid min-w-0 gap-1">
          <span className="pl-2 text-xs font-medium text-muted-foreground">{ex.rootNode === "root" ? "Folders" : "In this record"}</span>
          <FileTree
            tree={ex.tree}
            selectedNode={inTree ? ex.node : ""}
            selectedFolder={inTree ? ex.folder : ""}
            onSelect={(item) => { ex.navigate(item.node, item.folder || ""); onNavigate?.(); }}
            label="Folders"
          />
        </div>

        {showQuick && ex.categories.length > 0 && <CategoryNav ex={ex} onNavigate={onNavigate} />}
      </nav>
    </div>
    {showQuick && <div className="grid gap-2 border-t p-3">
      <StorageMeter storage={storage} />
      <p className="text-xs text-muted-foreground">Shared by your organization. Older versions count; archived files do not.</p>
    </div>}
  </>;
}

// Category filter in the sidebar; applies on top of the current place.
function CategoryNav({ ex, onNavigate }) {
  const [open, setOpen] = useState(Boolean(ex.state.category));
  return <Collapsible open={open} onOpenChange={setOpen} className="grid gap-0.5">
    <CollapsibleTrigger asChild>
      <button type="button" className="flex items-center justify-between rounded-md pr-1 pl-2 text-xs font-medium text-muted-foreground outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50">
        Categories <ChevronDown className={cn("size-3.5 transition-transform", open && "rotate-180")} />
      </button>
    </CollapsibleTrigger>
    <CollapsibleContent className="grid gap-0.5 pt-1">
      {ex.categories.map((category) => <NavItem
        key={category.value}
        icon={Tag}
        label={category.label}
        small
        active={ex.state.category === category.value}
        onClick={() => { ex.update({ category: ex.state.category === category.value ? "" : category.value }); onNavigate?.(); }}
      />)}
    </CollapsibleContent>
  </Collapsible>;
}

function NavItem({ icon: Icon, label, active, onClick, hint, small = false }) {
  return <button
    type="button"
    onClick={onClick}
    title={hint || label}
    aria-current={active ? "page" : undefined}
    className={cn(
      "flex w-full items-center gap-2 rounded-md px-2 text-left text-sm outline-none transition-colors hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring/50",
      small ? "h-7" : "h-8",
      active && "bg-accent font-medium text-accent-foreground",
    )}
  >
    <Icon className={cn("size-4 shrink-0", active ? "text-foreground" : "text-muted-foreground")} />
    <span className="truncate">{label}</span>
  </button>;
}

export function StorageMeter({ storage, compact = false }) {
  const usedPercent = Math.min(Number(storage?.usedPercent || 0), 100);
  const tone = usedPercent >= 90 ? "[&_[data-slot=progress-indicator]]:bg-destructive" : usedPercent >= 75 ? "[&_[data-slot=progress-indicator]]:bg-warning" : "";

  if (!storage) return <div className={cn("grid gap-1.5", compact ? "w-40" : "w-full")} aria-hidden="true"><Skeleton className="h-3 w-3/4" /><Skeleton className="h-1.5" /></div>;

  const meter = <div className={cn("flex items-center gap-2.5", compact ? "w-44" : "w-full")}>
    {compact && <Database className="size-4 shrink-0 text-muted-foreground" />}
    <div className="grid min-w-0 flex-1 gap-1.5 text-xs text-muted-foreground">
      {!compact && <span className="text-sm font-medium text-foreground">Storage</span>}
      <span><span className="font-medium text-foreground">{formatBytes(storage.usedBytes || 0)}</span> of {formatBytes(storage.quotaBytes || 0)} used</span>
      <Progress value={usedPercent} className={cn("h-1.5", tone)} aria-label="Storage used" />
    </div>
  </div>;

  return <Tooltip>
    <TooltipTrigger asChild><div tabIndex={compact ? 0 : -1} className="rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring/50">{meter}</div></TooltipTrigger>
    <TooltipContent>{formatBytes(storage.availableBytes || 0)} available · {usedPercent}% used</TooltipContent>
  </Tooltip>;
}

/* ---------- Toolbar and location ---------- */

function Toolbar({ ex, layout, mainWidth, onBrowse, uploadButton, newFolderButton }) {
  const narrow = layout === "narrow";
  const placeLabel = ex.info?.label || "this folder";
  const canScope = !ex.special && ex.node !== "root";
  const filterCount = ["category", "type", "by", "from", "to"].filter((key) => ex.state[key]).length + (ex.recursive ? 1 : 0);
  const collapsed = narrow || mainWidth < 820;
  const filters = <Filters ex={ex} stacked={collapsed} />;

  return <div className="grid gap-2 border-b px-4 py-3">
    <div className="flex flex-wrap items-center gap-2">
      {narrow && <Button variant="outline" size="sm" onClick={onBrowse}><PanelLeft /> Browse</Button>}
      {/* Narrow (phone-width) shells give search and scope full rows first; the buttons wrap underneath. */}
      <SearchInput value={ex.draftQ} onChange={ex.setDraftQ} placeholder="Search files, codes, uploaders…" className={cn("flex-1", narrow ? "order-first w-full min-w-0 basis-full sm:w-full" : "min-w-40 sm:w-auto sm:max-w-md")} />
      {canScope && <ToggleGroup type="single" variant="outline" size="sm" value={ex.state.scope === "everywhere" ? "everywhere" : "here"} onValueChange={(value) => value && ex.update({ scope: value === "everywhere" ? "everywhere" : "" })} aria-label="Search scope" className={cn(narrow && "order-first w-full min-w-0")}>
        <ToggleGroupItem value="here" className="max-w-48 min-w-0 px-2.5"><span className="truncate">In {placeLabel}</span></ToggleGroupItem>
        <ToggleGroupItem value="everywhere" className="px-2.5">Everywhere</ToggleGroupItem>
      </ToggleGroup>}
      <div className={narrow ? "contents" : "ml-auto flex flex-wrap items-center justify-end gap-2"}>
        {collapsed && <Popover>
          <PopoverTrigger asChild><Button variant="outline" size="sm" className={cn(filterCount && "border-primary/40 bg-primary/5")}><SlidersHorizontal /> Filters{filterCount ? ` · ${filterCount}` : ""}</Button></PopoverTrigger>
          <PopoverContent align="end" className="w-80">{filters}</PopoverContent>
        </Popover>}
        <ToggleGroup type="single" variant="outline" size="sm" value={ex.view} onValueChange={(value) => value && ex.update({ view: value, page: String(ex.page) })} aria-label="Layout">
          <ToggleGroupItem value="list" aria-label="List view" title="List view"><List /></ToggleGroupItem>
          <ToggleGroupItem value="grid" aria-label="Grid view" title="Grid view"><LayoutGrid /></ToggleGroupItem>
        </ToggleGroup>
        <Tooltip>
          <TooltipTrigger asChild><Button variant="outline" size="icon-sm" onClick={ex.refreshAll} disabled={ex.list.isRefreshing} aria-label="Reload files">{ex.list.isRefreshing ? <Spinner /> : <RefreshCw />}</Button></TooltipTrigger>
          <TooltipContent>Reload files</TooltipContent>
        </Tooltip>
        {!ex.special && newFolderButton}
        {uploadButton}
      </div>
    </div>
    {!collapsed && filters}
  </div>;
}

const DATE_PRESETS = [
  { days: 7, label: "Last 7 days" },
  { days: 30, label: "Last 30 days" },
  { days: 90, label: "Last 90 days" },
];

function Filters({ ex, stacked }) {
  const { options: members } = useTeamMemberOptions();
  const categoryOptions = ex.categories.map((category) => ({ value: category.value, label: category.label }));
  const dateLabel = ex.state.from || ex.state.to ? [ex.state.from ? formatDate(ex.state.from) : "…", ex.state.to ? formatDate(ex.state.to) : "today"].join(" – ") : "Any time";
  const since = (days) => new Date(Date.now() - days * 86400000).toISOString().slice(0, 10);
  const showSubfolders = !ex.special && !ex.everywhere;

  return <div className={cn("flex flex-wrap items-center gap-2", stacked && "grid")}>
    <FilterSelect label="Category" value={ex.state.category} onChange={(value) => ex.update({ category: value })} options={categoryOptions} allLabel="All categories" className={cn("h-8", stacked && "w-full")} />
    <FilterSelect label="Record" value={ex.state.type} onChange={(value) => ex.update({ type: value })} options={OWNER_TYPE_OPTIONS} allLabel="Any record" className={cn("h-8", stacked && "w-full")} />
    {members.length > 0 && <FilterCombobox label="Uploaded by" value={ex.state.by} onChange={(value) => ex.update({ by: value })} options={members} allLabel="Anyone" searchPlaceholder="Search team…" className={cn("h-8", stacked && "w-full")} />}
    <Popover>
      <PopoverTrigger asChild>
        <Button variant="outline" size="sm" className={cn("h-8 justify-start font-normal", (ex.state.from || ex.state.to) && "border-primary/40 bg-primary/5", stacked && "w-full")}>
          <CalendarRange /><span className="text-muted-foreground">Uploaded:</span> {dateLabel}
        </Button>
      </PopoverTrigger>
      <PopoverContent align="start" className="grid w-auto gap-3">
        <div className="flex flex-wrap gap-1.5">
          {DATE_PRESETS.map((preset) => <Button key={preset.days} variant="outline" size="sm" className="h-7" onClick={() => ex.update({ from: since(preset.days), to: "" })}>{preset.label}</Button>)}
          {(ex.state.from || ex.state.to) && <Button variant="ghost" size="sm" className="h-7" onClick={() => ex.update({ from: "", to: "" })}>Any time</Button>}
        </div>
        <DateRangeFilter label="Between" from={ex.state.from} to={ex.state.to} onChange={({ from, to }) => ex.update({ from, to })} />
      </PopoverContent>
    </Popover>
    {showSubfolders && <div className="flex items-center gap-2 px-1">
      <Switch id="include-subfolders" checked={ex.recursive} onCheckedChange={(checked) => ex.update({ sub: checked ? "1" : "0" })} />
      <Label htmlFor="include-subfolders" className="text-sm font-normal">Include subfolders</Label>
    </div>}
  </div>;
}

function LocationBar({ ex, narrow }) {
  const crumbs = ex.breadcrumb;
  const total = ex.list.total;
  const summary = ex.listEnabled ? `${total} ${total === 1 ? "file" : "files"}` : `${ex.folderItems.length} ${ex.folderItems.length === 1 ? "folder" : "folders"}`;

  if (ex.special || ex.everywhere) {
    const label = ex.special === "recent" ? "Recent" : ex.special === "archived" ? "Archived" : "Everywhere";
    return <div className="flex flex-wrap items-center justify-between gap-2">
      <div className="flex min-w-0 items-center gap-2">
        {ex.special === "recent" ? <Clock className="size-4 text-muted-foreground" /> : ex.special === "archived" ? <Archive className="size-4 text-muted-foreground" /> : <Files className="size-4 text-muted-foreground" />}
        <span className="font-medium">{label}</span>
        {ex.special === "recent" && <span className="text-xs text-muted-foreground">Uploaded or downloaded by you in the last 30 days</span>}
      </div>
      <div className="flex items-center gap-2">
        <span className="text-xs tabular-nums text-muted-foreground">{summary}</span>
        <Button variant="ghost" size="sm" onClick={() => ex.navigate(ex.node === "root" ? ex.rootNode : ex.node, ex.folder)}>Back to {ex.info?.label || "folders"}</Button>
      </div>
    </div>;
  }

  const current = crumbs[crumbs.length - 1];
  const parent = crumbs[crumbs.length - 2];

  return <div className="grid gap-2">
    <div className="flex flex-wrap items-center justify-between gap-2">
      {narrow
        ? <div className="flex min-w-0 items-center gap-1">
          {parent && <Button variant="ghost" size="sm" className="-ml-2 h-7 px-1.5" onClick={() => ex.navigate(parent.node, parent.folder)} aria-label={`Back to ${parent.label}`}><ChevronLeft /> <span className="max-w-28 truncate">{parent.label}</span></Button>}
          <span className="min-w-0 truncate font-medium">{current?.label}</span>
        </div>
        : <Breadcrumb className="min-w-0">
          <BreadcrumbList>
            {crumbs.map((item, index) => {
              const last = index === crumbs.length - 1;
              return <span key={`${item.node}-${item.folder}-${index}`} className="contents">
                {index > 0 && <BreadcrumbSeparator />}
                <BreadcrumbItem className="min-w-0">
                  {last ? <BreadcrumbPage className="truncate">{item.label}</BreadcrumbPage>
                    : <BreadcrumbLink asChild><button type="button" className="max-w-40 truncate" onClick={() => ex.navigate(item.node, item.folder)}>{item.label}</button></BreadcrumbLink>}
                </BreadcrumbItem>
              </span>;
            })}
          </BreadcrumbList>
        </Breadcrumb>}
      <div className="flex items-center gap-2">
        <span className="text-xs tabular-nums text-muted-foreground">{summary}</span>
        {ex.info?.recordUrl && ex.info.kind !== "organization" && <Button variant="outline" size="sm" className="h-7" asChild><Link href={ex.info.recordUrl}>Open record <ArrowUpRight /></Link></Button>}
      </div>
    </div>
    {ex.info?.alsoIn?.length > 0 && <div className="flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground">
      <span>Also on</span>
      {ex.info.alsoIn.map((item) => <Badge key={item.node} variant="secondary" asChild className="h-6 cursor-pointer font-normal">
        <button type="button" onClick={() => ex.navigate(item.node)}>{item.label || item.code}</button>
      </Badge>)}
    </div>}
  </div>;
}

/* ---------- Content ---------- */

function Content({ ex, layout, columns, showPath, onOpen, onOpenFolder, onFolderAction, onUpload, onNewFolder }) {
  const { list } = ex;
  const folders = ex.folderItems;
  const documents = ex.documents;
  const filtersActive = ["category", "type", "by", "from", "to"].some((key) => ex.state[key]);
  const grid = ex.view === "grid";
  const sort = { key: ex.state.sort, direction: ex.state.dir };
  const onSort = (key) => ex.update({ sort: key, dir: ex.state.sort === key && ex.state.dir === "asc" ? "desc" : "asc" });

  if (list.error && ex.listEnabled && !documents.length) return <ErrorState error={list.error} title="Files could not be loaded" onRetry={ex.refreshAll} compact />;
  if ((ex.listEnabled && list.isLoading) || (ex.foldersLoading && !documents.length)) return <FileListSkeleton layout={grid ? "grid" : "list"} rows={layout === "narrow" ? 4 : 6} />;

  if (!folders.length && !documents.length) {
    if (ex.state.q) {
      return <EmptyState
        compact
        filtered
        title={`No files match "${ex.state.q}"`}
        detail={ex.everywhere ? "Try a file name, a record code (PO-0012, SHP-0003), a category or an uploader." : `Nothing in ${ex.info?.label || "this folder"} matches. Search everywhere to look in every buyer, order and shipment.`}
        action={<>
          <Button variant="outline" size="sm" onClick={() => { ex.setDraftQ(""); ex.update({ q: "" }); }}>Clear search</Button>
          {!ex.everywhere && <Button size="sm" onClick={() => ex.update({ scope: "everywhere" })}>Search everywhere</Button>}
        </>}
      />;
    }
    if (filtersActive) return <EmptyState compact filtered title="No files match these filters" detail="Remove a filter, or include subfolders to look deeper." onClearFilters={() => ex.update({ category: "", type: "", by: "", from: "", to: "" })} />;
    if (ex.special === "archived") return <EmptyState compact icon={Archive} title="Nothing archived" detail="Archived files leave active lists but keep their history for audit. Archive a file from its ⋮ menu." />;
    if (ex.special === "recent") return <EmptyState compact icon={Clock} title="No recent files" detail="Files you upload or download show up here for 30 days." />;
    if (ex.uploadable) {
      return <EmptyState
        compact
        icon={CloudUpload}
        title={`No files in ${ex.folder ? ex.folder.split("/").pop() : ex.info?.label || "this folder"} yet`}
        detail={layout === "narrow" ? "Use Upload to add tech packs, lab dips, test reports, shipping documents…" : "Drop files here or use Upload. Tech packs, lab dips, test reports, shipping documents…"}
        action={<><Button size="sm" onClick={onUpload}><Upload /> Upload</Button>{onNewFolder && <Button variant="outline" size="sm" onClick={onNewFolder}><FolderPlus /> New folder</Button>}</>}
      />;
    }
    return <EmptyState compact icon={FileQuestion} title={`Nothing filed under ${ex.info?.label || "this folder"} yet`} detail="Files appear here once they are uploaded to a record inside it." />;
  }

  const pager = list.meta?.lastPage > 1 && <Pager page={ex.page} lastPage={list.meta.lastPage} total={list.total} perPage={list.meta.perPage} onPage={(page) => ex.update({ page: String(page) })} />;
  const containerHint = (ex.isContainer || ex.info?.counts?.rollup > 0) && !ex.recursive && !documents.length && folders.length > 0;

  return <div className={cn("grid gap-3 transition-opacity", list.isRefreshing && "opacity-60")}>
    {grid
      ? <FileGrid folders={folders} documents={documents} selection={ex.selection} activeId={ex.activeDocument?.id} onOpenFolder={onOpenFolder} onFolderAction={onFolderAction} onOpen={onOpen} showPath={showPath} />
      : <FileTable folders={folders} documents={documents} selection={ex.selection} activeId={ex.activeDocument?.id} sort={sort} onSort={onSort} onOpenFolder={onOpenFolder} onFolderAction={onFolderAction} onOpen={onOpen} columns={columns} showPath={showPath} />}
    {containerHint && <p className="text-xs text-muted-foreground">{ex.isContainer ? "Files live inside each record." : `${ex.info.counts.rollup} ${ex.info.counts.rollup === 1 ? "file is" : "files are"} filed in the folders below.`} Open a folder, or turn on <button type="button" className="font-medium text-foreground underline-offset-2 hover:underline" onClick={() => ex.update({ sub: "1" })}>Include subfolders</button> to list them all here.</p>}
    {pager}
  </div>;
}

function Pager({ page, lastPage, total, perPage, onPage }) {
  const first = (page - 1) * perPage + 1;
  const last = Math.min(total, page * perPage);
  return <nav className="flex items-center justify-between gap-2" aria-label="Pages">
    <span className="text-xs tabular-nums text-muted-foreground">{first}–{last} of {total}</span>
    <div className="flex items-center gap-1">
      <Button variant="outline" size="sm" disabled={page <= 1} onClick={() => onPage(page - 1)}><ChevronLeft /> Previous</Button>
      <span className="px-2 text-xs tabular-nums text-muted-foreground">Page {page} of {lastPage}</span>
      <Button variant="outline" size="sm" disabled={page >= lastPage} onClick={() => onPage(page + 1)}>Next <ChevronRight /></Button>
    </div>
  </nav>;
}

// Files stored on other records that this record uses. They are links, not copies.
function LinkedHere({ ex, columns, onOpen }) {
  const [open, setOpen] = useState(false);
  const count = ex.linkedDocuments.length;
  if (!count) return null;

  return <Collapsible open={open} onOpenChange={setOpen} className="rounded-lg border">
    <CollapsibleTrigger asChild>
      <button type="button" className="flex w-full items-center gap-2 px-3 py-2.5 text-left outline-none hover:bg-accent/50 focus-visible:ring-2 focus-visible:ring-ring/50">
        <Link2 className="size-4 shrink-0 text-muted-foreground" />
        <span className="text-sm font-medium">Linked here · {count}</span>
        <span className="hidden min-w-0 truncate text-xs text-muted-foreground sm:inline">Files stored elsewhere that this record uses. They are not copies.</span>
        <ChevronDown className={cn("ml-auto size-4 shrink-0 text-muted-foreground transition-transform", open && "rotate-180")} />
      </button>
    </CollapsibleTrigger>
    <CollapsibleContent className="border-t p-3">
      <FileTable documents={ex.linkedDocuments} activeId={ex.activeDocument?.id} onOpen={onOpen} columns={{ ...columns, user: false }} showPath />
    </CollapsibleContent>
  </Collapsible>;
}

function BulkBar({ documents, pending, onMove, onCategory, onArchive, onClear }) {
  const archivable = documents.filter((document) => document.permissions?.delete && document.status !== "deleted");
  const movable = documents.filter((document) => document.permissions?.move && document.status !== "deleted");
  const editable = documents.filter((document) => document.permissions?.update && document.status !== "deleted");
  const downloadable = documents.filter((document) => document.permissions?.download && document.status !== "deleted");
  const zipBytes = downloadable.reduce((sum, document) => sum + (Number(document.fileSize) || 0), 0);
  const zipReason = !downloadable.length ? "You cannot download the selected files."
    : downloadable.length > ZIP_LIMIT ? `Download up to ${ZIP_LIMIT} files at a time.`
      : zipBytes > ZIP_MAX_BYTES ? "The selected files are more than 200 MB together. Download them in smaller groups." : null;
  // One file downloads as itself; several come as one zip.
  const downloadHref = downloadable.length === 1 ? documentDownloadUrl(downloadable[0].id) : filesZipUrl(downloadable.map((document) => document.id));

  return <div className="flex flex-wrap items-center gap-2 rounded-lg border bg-muted/50 px-3 py-2" role="region" aria-label="Bulk actions">
    <span className="mr-auto text-sm font-medium">{documents.length} selected</span>
    {zipReason
      ? <ExplainedButton variant="outline" size="sm" reason={zipReason}><Download /> Download</ExplainedButton>
      : <Button variant="outline" size="sm" asChild><a href={downloadHref}><Download /> Download{downloadable.length > 1 ? " zip" : ""}</a></Button>}
    <ExplainedButton variant="outline" size="sm" reason={movable.length ? null : "You cannot move the selected files."} onClick={onMove} disabled={pending === "move"}><FolderInput /> Move to…</ExplainedButton>
    <ExplainedButton variant="outline" size="sm" reason={editable.length ? null : "You cannot edit the selected files."} onClick={onCategory}><Tag /> Set category…</ExplainedButton>
    <ExplainedButton variant="outline" size="sm" reason={archivable.length ? null : "You cannot archive the selected files, or they are already archived."} onClick={onArchive} disabled={pending === "archive"}>{pending === "archive" ? <Spinner /> : <Archive />} Archive</ExplainedButton>
    <Button variant="ghost" size="sm" onClick={onClear}>Clear selection</Button>
  </div>;
}

function DropOverlay({ uploadable, target, onLeave, onDrop }) {
  return <div
    className={cn("absolute inset-2 z-20 grid place-items-center rounded-xl border-2 border-dashed backdrop-blur-[1px]", uploadable ? "border-primary bg-background/85" : "border-border bg-background/90")}
    onDragOver={(event) => { event.preventDefault(); event.dataTransfer.dropEffect = uploadable ? "copy" : "none"; }}
    onDragLeave={(event) => { if (!event.currentTarget.contains(event.relatedTarget)) onLeave(); }}
    onDrop={(event) => { event.preventDefault(); onDrop(Array.from(event.dataTransfer.files || [])); }}
  >
    <div className="grid max-w-sm justify-items-center gap-2 p-6 text-center">
      <CloudUpload className={cn("size-9", uploadable ? "text-foreground" : "text-muted-foreground")} />
      <p className="text-sm font-medium">{uploadable ? `Drop to upload to ${target}` : "Open a record folder to upload"}</p>
      <p className="text-xs text-muted-foreground">{uploadable ? "You can set titles and a category before saving." : "Files always belong to a record: a buyer, inquiry, style, PO, shipment and so on."}</p>
    </div>
  </div>;
}

