"use client";

// ─────────────────────────────────────────────────────────────────────────────
// AssetBucketBrowser — single picker used everywhere in HQ Command.
//
// What's in the box:
//   • Bucket tabs (Media / Videos / Renders / etc.) per scope. Each tab
//     maps to an on-disk subfolder. Public-facing layout unchanged.
//   • Upload via drag-drop, file picker or paste-URL.
//   • Bulk select with click + Shift-click + Cmd/Ctrl-click. Bulk delete
//     and bulk move-to-bucket from the toolbar.
//   • Drag a file onto another bucket tab to MOVE it (PATCH /api/assets).
//   • Double-click a filename to rename in place.
//   • Search, grid/list views, copy URL per file.
//   • Toast + confirm via the global HqUiProvider — no browser popups.
// ─────────────────────────────────────────────────────────────────────────────

import { useState, useEffect, useRef, useCallback, useMemo } from "react";
import {
  X, FolderOpen, Upload, File, Grid3X3, LayoutList, Copy, Check,
  Image as ImageIcon, Video, Box, Search, Loader2, Trash2, Info,
  ArrowRightLeft, Pencil, CheckSquare, Square,
} from "lucide-react";
import { useHqUi } from "@/components/hq/Toast";

export interface AssetItem {
  name: string;
  type: "file" | "folder";
  mediaType?: string;
  extension?: string;
  path: string;
  publicUrl?: string;
  size?: string;
  childCount?: number;
}

export interface SelectedAsset {
  name: string;
  publicUrl: string;
  mediaType: string;
  path: string;
}

type Scope = "cases" | "applications" | "news" | "parts" | "footage" | "branding";

export interface BucketDef {
  id: string;
  /** Folder under /{scope}/{slug}/. Empty string means the entity root. */
  path: string;
  label: string;
  description: string;
  icon: typeof ImageIcon;
  /** Suggested file types. Used for the <input accept=...> hint, not enforced. */
  accept: string;
  accentColor: string;
}

const BUCKETS_BY_SCOPE: Record<Scope, BucketDef[]> = {
  cases: [
    { id: "media",   path: "",        label: "Media",   description: "Cover and gallery images at the case root",           icon: ImageIcon, accept: "image/*",                  accentColor: "#00F0FF" },
    { id: "videos",  path: "videos",  label: "Videos",  description: "MP4 clips of the installation in operation",          icon: Video,     accept: "video/*",                  accentColor: "#4DA6FF" },
    { id: "models",  path: "models",  label: "3D Models", description: "GLB/USDZ for AR viewer and 3D display",            icon: Box,       accept: ".glb,.gltf,.usdz",         accentColor: "#A855F7" },
    { id: "renders", path: "renders", label: "Renders", description: "3D rendered images of the equipment",                 icon: Box,       accept: "image/*",                  accentColor: "#FF6B9D" },
  ],
  applications: [
    { id: "media",   path: "",        label: "Media",   description: "Hero, blueprints, machines — files at the application root", icon: ImageIcon, accept: "image/*,video/*", accentColor: "#9333EA" },
    { id: "videos",  path: "videos",  label: "Videos",  description: "Demo videos for this application",                          icon: Video,     accept: "video/*",         accentColor: "#4DA6FF" },
    { id: "renders", path: "renders", label: "Renders", description: "3D rendered images",                                        icon: Box,       accept: "image/*",         accentColor: "#FF6B9D" },
  ],
  news: [
    { id: "media", path: "", label: "Media", description: "Cover image and gallery photos", icon: ImageIcon, accept: "image/*", accentColor: "#0A66C2" },
  ],
  parts: [
    { id: "media",   path: "",        label: "Media",   description: "Part photos and product shots", icon: ImageIcon, accept: "image/*", accentColor: "#F59E0B" },
    { id: "renders", path: "renders", label: "Renders", description: "3D renders of the component",   icon: Box,       accept: "image/*", accentColor: "#FF6B9D" },
  ],
  footage: [
    { id: "media", path: "", label: "Hero Reel", description: "Hero carousel images and videos", icon: ImageIcon, accept: "image/*,video/*", accentColor: "#FF6B9D" },
  ],
  branding: [
    { id: "media", path: "", label: "Brand Assets", description: "Favicons, logos, OG images", icon: ImageIcon, accept: "image/*", accentColor: "#FF6B9D" },
  ],
};

function bucketHint(bucket: BucketDef, fileName: string): string | null {
  const ext = (fileName.split(".").pop() || "").toLowerCase();
  const isImage = ["jpg", "jpeg", "png", "webp", "gif", "svg", "avif"].includes(ext);
  const isVideo = ["mp4", "webm", "mov"].includes(ext);
  const isModel = ["glb", "gltf", "usdz"].includes(ext);
  if (bucket.id === "videos" && !isVideo) return "Videos bucket usually holds .mp4 — this file may not display correctly.";
  if (bucket.id === "renders" && !isImage) return "Renders bucket expects images (PNG/JPG/WebP).";
  if (bucket.id === "models" && !isModel) return "Models bucket expects 3D files (.glb, .gltf, .usdz).";
  if (bucket.id === "media" && !isImage && !isVideo) return "Media bucket expects images or videos here.";
  return null;
}

export interface AssetBucketBrowserProps {
  slug: string;
  scope?: Scope;
  isOpen: boolean;
  onClose: () => void;
  onSelect: (item: SelectedAsset) => void;
  accentColor?: string;
  initialPath?: string;
  title?: string;
}

// Shape used internally to track drag state across the modal.
interface DragPayload {
  paths: string[];
  fromBucketId: string;
}

export default function AssetBucketBrowser({
  slug, scope = "cases", isOpen, onClose, onSelect,
  accentColor = "#00F0FF", initialPath = "", title,
}: AssetBucketBrowserProps) {
  const ui = useHqUi();
  const buckets = BUCKETS_BY_SCOPE[scope] || BUCKETS_BY_SCOPE.cases;

  const defaultBucketId = useMemo(() => {
    const match = buckets.find((b) => b.path === initialPath);
    return match ? match.id : buckets[0].id;
  }, [buckets, initialPath]);

  const [activeBucketId, setActiveBucketId] = useState(defaultBucketId);
  const activeBucket = buckets.find((b) => b.id === activeBucketId) || buckets[0];

  const [items, setItems] = useState<AssetItem[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [isUploading, setIsUploading] = useState(false);
  const [uploadProgress, setUploadProgress] = useState("");
  const [isDragOverDropZone, setIsDragOverDropZone] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
  const [copiedPath, setCopiedPath] = useState<string | null>(null);

  // Selection + drag + rename state
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [lastSelected, setLastSelected] = useState<string | null>(null);
  const [renaming, setRenaming] = useState<{ path: string; value: string } | null>(null);
  const [moveBusy, setMoveBusy] = useState(false);
  const [overBucketId, setOverBucketId] = useState<string | null>(null);
  const dragPayloadRef = useRef<DragPayload | null>(null);

  const fileInputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (isOpen) {
      setActiveBucketId(defaultBucketId);
      setSearchQuery("");
      setError(null);
      setSelected(new Set());
      setLastSelected(null);
      setRenaming(null);
    }
  }, [isOpen, defaultBucketId]);

  // Reset selection when switching buckets
  useEffect(() => {
    setSelected(new Set());
    setLastSelected(null);
    setRenaming(null);
  }, [activeBucketId]);

  const fetchItems = useCallback(async () => {
    setIsLoading(true);
    setError(null);
    try {
      const params = new URLSearchParams({ scope, slug, path: activeBucket.path });
      const res = await fetch(`/api/assets?${params}`);
      const data = await res.json();
      if (data.success) setItems(data.items.filter((i: AssetItem) => i.type === "file"));
      else setError(data.error || "Failed to load");
    } catch {
      setError("Connection error");
    }
    setIsLoading(false);
  }, [scope, slug, activeBucket.path]);

  useEffect(() => {
    if (isOpen && slug) fetchItems();
  }, [isOpen, fetchItems, slug]);

  // ─── Upload ─────────────────────────────────────────────────────
  const uploadFile = async (file: File) => {
    setIsUploading(true);
    setUploadProgress(`Uploading ${file.name}…`);
    try {
      const fd = new FormData();
      fd.append("scope", scope);
      fd.append("slug", slug);
      fd.append("path", activeBucket.path);
      fd.append("file", file);
      fd.append("optimize", "1");
      const res = await fetch("/api/assets", { method: "POST", body: fd });
      const data = await res.json();
      if (data.success) {
        const f = data.file;
        if (f.optimized && f.savedBytes > 0) {
          const pct = Math.round((f.savedBytes / f.originalBytes) * 100);
          setUploadProgress(`✓ ${f.name} (−${pct}% optimized)`);
        } else {
          setUploadProgress(`✓ ${f.name}`);
        }
        await fetchItems();
        setTimeout(() => setUploadProgress(""), 1500);
      } else {
        setUploadProgress(`✗ ${data.error}`);
        setTimeout(() => setUploadProgress(""), 4000);
      }
    } catch (err: any) {
      setUploadProgress(`✗ ${err.message || "Upload failed"}`);
      setTimeout(() => setUploadProgress(""), 4000);
    }
    setIsUploading(false);
  };

  const handleFiles = (files: FileList | null) => {
    if (!files) return;
    Array.from(files).forEach(uploadFile);
  };

  // ─── Bulk delete ────────────────────────────────────────────────
  const deleteFiles = async (paths: string[]) => {
    if (paths.length === 0) return;
    const ok = await ui.confirm({
      title: paths.length === 1 ? "Delete file" : `Delete ${paths.length} files`,
      message: paths.length === 1
        ? `Permanently delete "${paths[0].split("/").pop()}"? This cannot be undone.`
        : `Permanently delete ${paths.length} files? This cannot be undone.`,
      confirmLabel: "Delete",
      destructive: true,
    });
    if (!ok) return;

    try {
      const res = await fetch("/api/assets", {
        method: "DELETE",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ scope, slug, filePaths: paths }),
      });
      const data = await res.json();
      if (data.success) {
        ui.toast(
          data.deleted.length === 1 ? "File deleted." : `${data.deleted.length} files deleted.`,
          "success"
        );
        if (data.failed?.length > 0) ui.toast(`${data.failed.length} files failed: ${data.failed.map((f: any) => f.reason).join(", ")}`, "error");
      } else {
        ui.toast(data.error || "Delete failed", "error");
      }
      setSelected(new Set());
      await fetchItems();
    } catch (err: any) {
      ui.toast(err.message || "Delete failed", "error");
    }
  };

  // ─── Move (single file or bulk) ─────────────────────────────────
  const moveFiles = async (paths: string[], toBucketId: string) => {
    if (paths.length === 0) return;
    const target = buckets.find((b) => b.id === toBucketId);
    if (!target) return;

    setMoveBusy(true);
    let okCount = 0;
    let failCount = 0;
    for (const fromPath of paths) {
      const filename = fromPath.split("/").pop()!;
      const toPath = target.path ? `${target.path}/${filename}` : filename;
      if (fromPath === toPath) continue;
      try {
        const res = await fetch("/api/assets", {
          method: "PATCH",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ scope, slug, fromPath, toPath }),
        });
        const data = await res.json();
        if (data.success) okCount++; else failCount++;
      } catch {
        failCount++;
      }
    }
    setMoveBusy(false);
    setSelected(new Set());

    if (okCount > 0) ui.toast(`Moved ${okCount} file${okCount > 1 ? "s" : ""} to ${target.label}.`, "success");
    if (failCount > 0) ui.toast(`${failCount} file${failCount > 1 ? "s" : ""} could not be moved (name conflict?).`, "error");
    await fetchItems();
  };

  // ─── Rename ─────────────────────────────────────────────────────
  const submitRename = async () => {
    if (!renaming) return;
    const newName = renaming.value.trim();
    const oldName = renaming.path.split("/").pop()!;
    if (!newName || newName === oldName) {
      setRenaming(null);
      return;
    }
    const cleanName = newName.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9._-]/g, "");
    if (!cleanName) {
      ui.toast("Invalid filename.", "error");
      return;
    }

    const dir = renaming.path.includes("/") ? renaming.path.slice(0, renaming.path.lastIndexOf("/")) : "";
    const toPath = dir ? `${dir}/${cleanName}` : cleanName;

    try {
      const res = await fetch("/api/assets", {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ scope, slug, fromPath: renaming.path, toPath }),
      });
      const data = await res.json();
      if (data.success) {
        ui.toast("Renamed.", "success");
        setRenaming(null);
        await fetchItems();
      } else {
        ui.toast(data.error || "Rename failed", "error");
      }
    } catch (err: any) {
      ui.toast(err.message || "Rename failed", "error");
    }
  };

  // ─── Selection helpers ──────────────────────────────────────────
  const toggleSelect = (path: string, e?: React.MouseEvent) => {
    const next = new Set(selected);
    if (e?.shiftKey && lastSelected) {
      // Range select
      const ids = filtered.map((i) => i.path);
      const a = ids.indexOf(lastSelected);
      const b = ids.indexOf(path);
      if (a >= 0 && b >= 0) {
        const [start, end] = a < b ? [a, b] : [b, a];
        for (let i = start; i <= end; i++) next.add(ids[i]);
      }
    } else if (e?.metaKey || e?.ctrlKey) {
      if (next.has(path)) next.delete(path); else next.add(path);
    } else {
      // Plain click toggles, leaves others alone
      if (next.has(path)) next.delete(path); else next.add(path);
    }
    setSelected(next);
    setLastSelected(path);
  };

  const selectAll = () => {
    if (selected.size === filtered.length) setSelected(new Set());
    else setSelected(new Set(filtered.map((i) => i.path)));
  };

  // ─── Drag-between-buckets ───────────────────────────────────────
  const onItemDragStart = (e: React.DragEvent, item: AssetItem) => {
    // If the dragged item isn't in selection, drag just that one. If it is,
    // drag the whole selection.
    let paths: string[];
    if (selected.has(item.path)) paths = Array.from(selected);
    else { paths = [item.path]; setSelected(new Set([item.path])); }
    dragPayloadRef.current = { paths, fromBucketId: activeBucketId };
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/plain", paths.join(","));
  };

  const onTabDragOver = (e: React.DragEvent, bucketId: string) => {
    if (!dragPayloadRef.current) return;
    if (dragPayloadRef.current.fromBucketId === bucketId) return;
    e.preventDefault();
    e.dataTransfer.dropEffect = "move";
    setOverBucketId(bucketId);
  };

  const onTabDragLeave = () => setOverBucketId(null);

  const onTabDrop = async (e: React.DragEvent, bucketId: string) => {
    e.preventDefault();
    setOverBucketId(null);
    const payload = dragPayloadRef.current;
    dragPayloadRef.current = null;
    if (!payload || payload.fromBucketId === bucketId) return;
    await moveFiles(payload.paths, bucketId);
  };

  // ─── Misc ────────────────────────────────────────────────────────
  const handlePick = (item: AssetItem) => {
    onSelect({
      name: item.name,
      publicUrl: item.publicUrl || `/${scope}/${slug}/${item.path}`,
      mediaType: item.mediaType || "unknown",
      path: item.path,
    });
  };

  const copyPath = (item: AssetItem) => {
    const url = item.publicUrl || `/${scope}/${slug}/${item.path}`;
    navigator.clipboard.writeText(url);
    setCopiedPath(item.path);
    setTimeout(() => setCopiedPath(null), 1500);
  };

  const filtered = searchQuery
    ? items.filter((i) => i.name.toLowerCase().includes(searchQuery.toLowerCase()))
    : items;

  const otherBuckets = buckets.filter((b) => b.id !== activeBucketId);
  const hasSelection = selected.size > 0;

  if (!isOpen) return null;

  return (
    <div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm" onClick={onClose}>
      <div
        className="relative w-full max-w-6xl h-[80vh] bg-[#111] border border-white/10 rounded-3xl shadow-2xl flex flex-col overflow-hidden"
        onClick={(e) => e.stopPropagation()}
        style={{ ["--accent" as any]: accentColor }}
      >
        {/* Header */}
        <div className="flex items-center justify-between px-6 py-4 border-b border-white/5 flex-shrink-0">
          <div className="flex items-center gap-3">
            <FolderOpen size={18} style={{ color: accentColor }} />
            <div>
              <div className="text-sm font-medium text-white">{title || "Assets"}</div>
              <div className="text-[10px] text-[#86868B] font-mono">/{scope}/{slug}{activeBucket.path ? `/${activeBucket.path}` : ""}</div>
            </div>
          </div>
          <button onClick={onClose} className="p-2 rounded-lg hover:bg-white/5 text-[#86868B] hover:text-white transition-colors">
            <X size={18} />
          </button>
        </div>

        {/* Bucket tabs (drop targets while dragging) */}
        <div className="flex gap-1 px-6 pt-4 border-b border-white/5">
          {buckets.map((b) => {
            const Icon = b.icon;
            const isActive = activeBucketId === b.id;
            const isDropTarget = overBucketId === b.id;
            return (
              <button
                key={b.id}
                onClick={() => setActiveBucketId(b.id)}
                onDragOver={(e) => onTabDragOver(e, b.id)}
                onDragLeave={onTabDragLeave}
                onDrop={(e) => onTabDrop(e, b.id)}
                className={`flex items-center gap-2 px-4 py-2.5 text-sm font-medium border-b-2 transition-all -mb-px ${
                  isDropTarget ? "border-emerald-400 bg-emerald-500/10 text-emerald-300"
                  : isActive ? "border-[var(--accent)] text-white"
                  : "border-transparent text-[#86868B] hover:text-white hover:bg-white/[0.02]"
                }`}
                title={b.description}
              >
                <Icon size={14} style={{ color: isDropTarget ? "#34D399" : isActive ? b.accentColor : undefined }} />
                {b.label}
                {isDropTarget && <span className="text-[9px] uppercase tracking-widest">drop to move</span>}
              </button>
            );
          })}
        </div>

        {/* Bucket helper */}
        <div className="flex items-center gap-3 px-6 py-3 bg-white/[0.02] border-b border-white/5 text-xs text-[#86868B]">
          <Info size={12} className="flex-shrink-0" style={{ color: activeBucket.accentColor }} />
          <span className="leading-relaxed">{activeBucket.description}</span>
          {moveBusy && <span className="ml-auto flex items-center gap-1 text-[#00F0FF]"><Loader2 size={12} className="animate-spin" /> Moving…</span>}
        </div>

        {/* Toolbar — switches to bulk-action mode when items are selected */}
        <div className="flex items-center gap-2 px-6 py-3 border-b border-white/5 flex-shrink-0">
          {hasSelection ? (
            <>
              <button
                onClick={() => setSelected(new Set())}
                className="flex items-center gap-1.5 text-xs text-[#86868B] hover:text-white px-2 py-1.5"
              >
                <X size={13} /> {selected.size} selected
              </button>
              <div className="h-4 w-px bg-white/10" />
              <button
                onClick={() => deleteFiles(Array.from(selected))}
                className="flex items-center gap-1.5 bg-rose-500/10 text-rose-400 hover:bg-rose-500/20 px-3 py-1.5 rounded-lg text-xs font-medium transition-colors"
              >
                <Trash2 size={12} /> Delete
              </button>
              {otherBuckets.length > 0 && (
                <div className="flex items-center gap-1.5 text-xs text-[#86868B]">
                  <ArrowRightLeft size={12} /> Move to:
                  {otherBuckets.map((b) => (
                    <button
                      key={b.id}
                      onClick={() => moveFiles(Array.from(selected), b.id)}
                      disabled={moveBusy}
                      className="px-2 py-1 rounded text-[11px] font-medium hover:bg-white/5 disabled:opacity-50"
                      style={{ color: b.accentColor }}
                    >
                      {b.label}
                    </button>
                  ))}
                </div>
              )}
            </>
          ) : (
            <>
              <button
                onClick={selectAll}
                className="flex items-center gap-1.5 text-xs text-[#86868B] hover:text-white px-2 py-1.5"
                title="Select all (filtered)"
              >
                {selected.size === filtered.length && filtered.length > 0 ? <CheckSquare size={13} /> : <Square size={13} />}
                Select all
              </button>
              <div className="relative flex-1 max-w-md">
                <Search size={13} className="absolute left-3 top-1/2 -translate-y-1/2 text-[#86868B]" />
                <input
                  type="text"
                  value={searchQuery}
                  onChange={(e) => setSearchQuery(e.target.value)}
                  placeholder="Search files…"
                  className="w-full bg-black/40 border border-white/10 text-white text-xs rounded-lg pl-9 pr-3 py-2 outline-none focus:border-white/30"
                />
              </div>

              <div className="flex items-center gap-1 ml-auto">
                <button onClick={() => setViewMode("grid")} className={`p-2 rounded-lg ${viewMode === "grid" ? "bg-white/10 text-white" : "text-[#86868B] hover:bg-white/5"}`} title="Grid view">
                  <Grid3X3 size={14} />
                </button>
                <button onClick={() => setViewMode("list")} className={`p-2 rounded-lg ${viewMode === "list" ? "bg-white/10 text-white" : "text-[#86868B] hover:bg-white/5"}`} title="List view">
                  <LayoutList size={14} />
                </button>
              </div>

              <input
                ref={fileInputRef}
                type="file"
                accept={activeBucket.accept}
                multiple
                className="hidden"
                onChange={(e) => { handleFiles(e.target.files); e.target.value = ""; }}
              />
              <button
                onClick={() => fileInputRef.current?.click()}
                disabled={isUploading}
                className="inline-flex items-center gap-2 bg-[var(--accent)]/15 text-[var(--accent)] hover:bg-[var(--accent)]/25 px-3 py-2 rounded-lg text-xs font-medium transition-colors disabled:opacity-50"
              >
                {isUploading ? <Loader2 size={12} className="animate-spin" /> : <Upload size={12} />}
                Upload
              </button>
            </>
          )}
        </div>

        {/* Items area */}
        <div
          onDragEnter={(e) => { e.preventDefault(); if (!dragPayloadRef.current) setIsDragOverDropZone(true); }}
          onDragOver={(e) => e.preventDefault()}
          onDragLeave={() => setIsDragOverDropZone(false)}
          onDrop={(e) => {
            e.preventDefault();
            setIsDragOverDropZone(false);
            if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files);
          }}
          className={`flex-1 overflow-y-auto px-6 py-4 transition-colors ${
            isDragOverDropZone ? "bg-[var(--accent)]/5 border-2 border-dashed border-[var(--accent)]/30" : ""
          }`}
        >
          {uploadProgress && (
            <div className="mb-3 px-4 py-2.5 rounded-lg bg-white/[0.04] text-xs text-white border border-white/10 inline-block">
              {uploadProgress}
            </div>
          )}

          {isLoading ? (
            <div className="flex items-center justify-center py-12 text-[#86868B] text-sm">
              <Loader2 size={14} className="animate-spin mr-2" /> Loading…
            </div>
          ) : error ? (
            <div className="text-center py-12 text-rose-400 text-sm">{error}</div>
          ) : filtered.length === 0 ? (
            <div className="text-center py-12 text-[#86868B] text-sm">
              {searchQuery ? <>No files match &quot;{searchQuery}&quot;.</> : (
                <div className="space-y-2">
                  <activeBucket.icon size={32} className="mx-auto opacity-40" />
                  <p>No files in {activeBucket.label} yet.</p>
                  <p className="text-xs">Drop a file here or click Upload. Drag a file from another tab to move it in.</p>
                </div>
              )}
            </div>
          ) : viewMode === "grid" ? (
            <GridView
              items={filtered}
              selected={selected}
              renaming={renaming}
              copiedPath={copiedPath}
              onClickItem={(item, e) => {
                if (hasSelection || e.shiftKey || e.metaKey || e.ctrlKey) toggleSelect(item.path, e);
                else handlePick(item);
              }}
              onToggleSelect={(item, e) => toggleSelect(item.path, e)}
              onStartRename={(item) => setRenaming({ path: item.path, value: item.name })}
              onChangeRename={(value) => setRenaming((r) => r ? { ...r, value } : r)}
              onSubmitRename={submitRename}
              onCancelRename={() => setRenaming(null)}
              onCopy={copyPath}
              onDelete={(item) => deleteFiles([item.path])}
              onDragStart={onItemDragStart}
              hint={(name: string) => bucketHint(activeBucket, name)}
            />
          ) : (
            <ListView
              items={filtered}
              selected={selected}
              renaming={renaming}
              copiedPath={copiedPath}
              onClickItem={(item, e) => {
                if (hasSelection || e.shiftKey || e.metaKey || e.ctrlKey) toggleSelect(item.path, e);
                else handlePick(item);
              }}
              onToggleSelect={(item, e) => toggleSelect(item.path, e)}
              onStartRename={(item) => setRenaming({ path: item.path, value: item.name })}
              onChangeRename={(value) => setRenaming((r) => r ? { ...r, value } : r)}
              onSubmitRename={submitRename}
              onCancelRename={() => setRenaming(null)}
              onCopy={copyPath}
              onDelete={(item) => deleteFiles([item.path])}
              onDragStart={onItemDragStart}
              hint={(name: string) => bucketHint(activeBucket, name)}
            />
          )}
        </div>

        {/* Footer hint */}
        <div className="px-6 py-2.5 border-t border-white/5 text-[10px] text-[#86868B] flex items-center gap-4">
          <span><kbd className="bg-white/5 border border-white/10 rounded px-1.5 py-0.5 font-mono">Click</kbd> open</span>
          <span><kbd className="bg-white/5 border border-white/10 rounded px-1.5 py-0.5 font-mono">⇧Click</kbd> select range</span>
          <span><kbd className="bg-white/5 border border-white/10 rounded px-1.5 py-0.5 font-mono">⌘Click</kbd> add to selection</span>
          <span><kbd className="bg-white/5 border border-white/10 rounded px-1.5 py-0.5 font-mono">2× click</kbd> rename</span>
          <span>Drag onto another tab to move</span>
        </div>
      </div>
    </div>
  );
}

// ─── Grid view ────────────────────────────────────────────────────────────
interface ViewProps {
  items: AssetItem[];
  selected: Set<string>;
  renaming: { path: string; value: string } | null;
  copiedPath: string | null;
  onClickItem: (item: AssetItem, e: React.MouseEvent) => void;
  onToggleSelect: (item: AssetItem, e: React.MouseEvent) => void;
  onStartRename: (item: AssetItem) => void;
  onChangeRename: (value: string) => void;
  onSubmitRename: () => void;
  onCancelRename: () => void;
  onCopy: (item: AssetItem) => void;
  onDelete: (item: AssetItem) => void;
  onDragStart: (e: React.DragEvent, item: AssetItem) => void;
  hint: (name: string) => string | null;
}

function GridView(props: ViewProps) {
  return (
    <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3">
      {props.items.map((item) => {
        const isSel = props.selected.has(item.path);
        const isRen = props.renaming?.path === item.path;
        const warning = props.hint(item.name);
        return (
          <div
            key={item.path}
            draggable
            onDragStart={(e) => props.onDragStart(e, item)}
            className={`group bg-white/[0.02] border rounded-xl overflow-hidden transition-all ${
              isSel ? "border-[#00F0FF] ring-2 ring-[#00F0FF]/30" : "border-white/5 hover:border-white/15"
            }`}
          >
            {/* Selection checkbox overlay */}
            <button
              onClick={(e) => { e.stopPropagation(); props.onToggleSelect(item, e); }}
              className={`absolute m-2 z-10 w-5 h-5 rounded flex items-center justify-center transition-all ${
                isSel ? "bg-[#00F0FF] text-black opacity-100"
                : "bg-black/60 text-white/40 opacity-0 group-hover:opacity-100 hover:bg-black/80"
              }`}
              title="Select"
            >
              {isSel && <Check size={12} />}
            </button>

            <button
              onClick={(e) => props.onClickItem(item, e)}
              onDoubleClick={(e) => { e.stopPropagation(); props.onStartRename(item); }}
              className="block w-full aspect-video bg-black overflow-hidden cursor-pointer relative"
            >
              {item.mediaType === "image" && item.publicUrl ? (
                // eslint-disable-next-line @next/next/no-img-element
                <img src={item.publicUrl} alt={item.name} className="w-full h-full object-cover group-hover:scale-105 transition-transform" loading="lazy" />
              ) : item.mediaType === "video" ? (
                <div className="w-full h-full flex items-center justify-center bg-blue-500/5">
                  <Video size={28} className="text-blue-400/60" />
                </div>
              ) : item.mediaType === "model" ? (
                <div className="w-full h-full flex items-center justify-center bg-purple-500/5">
                  <Box size={28} className="text-purple-400/60" />
                </div>
              ) : (
                <div className="w-full h-full flex items-center justify-center">
                  <File size={28} className="text-[#86868B]/50" />
                </div>
              )}
            </button>

            <div className="p-2.5">
              {isRen ? (
                <input
                  autoFocus
                  type="text"
                  value={props.renaming!.value}
                  onChange={(e) => props.onChangeRename(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") props.onSubmitRename();
                    else if (e.key === "Escape") props.onCancelRename();
                  }}
                  onBlur={props.onSubmitRename}
                  className="w-full bg-black/60 border border-[#00F0FF]/40 text-white text-[11px] font-mono rounded px-2 py-1 outline-none"
                />
              ) : (
                <div
                  onDoubleClick={() => props.onStartRename(item)}
                  className="text-[11px] text-white truncate font-medium cursor-text"
                  title="Double-click to rename"
                >
                  {item.name}
                </div>
              )}

              <div className="flex items-center justify-between mt-1.5">
                <span className="text-[9px] text-[#86868B]">{item.size}</span>
                <div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
                  <button
                    onClick={(e) => { e.stopPropagation(); props.onStartRename(item); }}
                    className="p-1 rounded text-[#86868B] hover:text-white hover:bg-white/5"
                    title="Rename"
                  >
                    <Pencil size={11} />
                  </button>
                  <button
                    onClick={(e) => { e.stopPropagation(); props.onCopy(item); }}
                    className="p-1 rounded text-[#86868B] hover:text-white hover:bg-white/5"
                    title="Copy URL"
                  >
                    {props.copiedPath === item.path ? <Check size={11} className="text-emerald-400" /> : <Copy size={11} />}
                  </button>
                  <button
                    onClick={(e) => { e.stopPropagation(); props.onDelete(item); }}
                    className="p-1 rounded text-rose-400 hover:bg-rose-500/10"
                    title="Delete"
                  >
                    <Trash2 size={11} />
                  </button>
                </div>
              </div>
              {warning && (
                <div className="text-[9px] text-amber-400/80 mt-1 leading-tight">{warning}</div>
              )}
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─── List view ────────────────────────────────────────────────────────────
function ListView(props: ViewProps) {
  return (
    <div className="space-y-1">
      {props.items.map((item) => {
        const isSel = props.selected.has(item.path);
        const isRen = props.renaming?.path === item.path;
        return (
          <div
            key={item.path}
            draggable
            onDragStart={(e) => props.onDragStart(e, item)}
            className={`flex items-center gap-3 px-3 py-2 rounded-lg group ${
              isSel ? "bg-[#00F0FF]/10 border border-[#00F0FF]/30" : "hover:bg-white/[0.02] border border-transparent"
            }`}
          >
            <button
              onClick={(e) => { e.stopPropagation(); props.onToggleSelect(item, e); }}
              className={`w-4 h-4 rounded flex items-center justify-center flex-shrink-0 ${
                isSel ? "bg-[#00F0FF] text-black"
                : "border border-white/20 text-transparent hover:border-white/40"
              }`}
            >
              {isSel && <Check size={10} />}
            </button>

            <button
              onClick={(e) => props.onClickItem(item, e)}
              onDoubleClick={() => props.onStartRename(item)}
              className="flex-shrink-0 w-12 h-12 rounded bg-black overflow-hidden"
            >
              {item.mediaType === "image" && item.publicUrl ? (
                // eslint-disable-next-line @next/next/no-img-element
                <img src={item.publicUrl} alt={item.name} className="w-full h-full object-cover" loading="lazy" />
              ) : item.mediaType === "video" ? (
                <div className="w-full h-full flex items-center justify-center bg-blue-500/5"><Video size={16} className="text-blue-400/60" /></div>
              ) : item.mediaType === "model" ? (
                <div className="w-full h-full flex items-center justify-center bg-purple-500/5"><Box size={16} className="text-purple-400/60" /></div>
              ) : (
                <div className="w-full h-full flex items-center justify-center"><File size={16} className="text-[#86868B]/50" /></div>
              )}
            </button>

            <div className="flex-1 min-w-0">
              {isRen ? (
                <input
                  autoFocus
                  type="text"
                  value={props.renaming!.value}
                  onChange={(e) => props.onChangeRename(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") props.onSubmitRename();
                    else if (e.key === "Escape") props.onCancelRename();
                  }}
                  onBlur={props.onSubmitRename}
                  className="w-full bg-black/60 border border-[#00F0FF]/40 text-white text-xs font-mono rounded px-2 py-1 outline-none"
                />
              ) : (
                <button
                  onClick={(e) => props.onClickItem(item, e)}
                  onDoubleClick={() => props.onStartRename(item)}
                  className="block text-left w-full"
                >
                  <div className="text-xs text-white font-medium truncate">{item.name}</div>
                  <div className="text-[10px] text-[#86868B]">{item.size}</div>
                </button>
              )}
            </div>

            <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
              <button
                onClick={() => props.onStartRename(item)}
                className="p-1.5 rounded text-[#86868B] hover:text-white hover:bg-white/5"
                title="Rename"
              >
                <Pencil size={12} />
              </button>
              <button
                onClick={() => props.onCopy(item)}
                className="p-1.5 rounded text-[#86868B] hover:text-white hover:bg-white/5"
                title="Copy URL"
              >
                {props.copiedPath === item.path ? <Check size={12} className="text-emerald-400" /> : <Copy size={12} />}
              </button>
              <button
                onClick={() => props.onDelete(item)}
                className="p-1.5 rounded text-rose-400 hover:bg-rose-500/10"
                title="Delete"
              >
                <Trash2 size={12} />
              </button>
            </div>
          </div>
        );
      })}
    </div>
  );
}
