import { useCallback, useEffect, useRef, useState } from 'react';
import {
  ArrowLeft,
  Check,
  ChevronLeft,
  ChevronRight,
  Copy,
  Download,
  Eye,
  File,
  FileText,
  Folder,
  FolderPlus,
  HardDrive,
  Home,
  Image as ImageIcon,
  Loader2,
  MoreVertical,
  Move,
  Pencil,
  RefreshCw,
  Search,
  Trash2,
  Upload,
  X,
} from 'lucide-react';
import {
  copyPath,
  createFolder,
  deletePath,
  getFileContents,
  getWebDAVBaseUrl,
  humanFileSize,
  joinPath,
  listDirectory,
  movePath,
  normalizePath,
  putFileContents,
  splitPath,
  type EntryInfo,
} from '../webdav';
import { toast } from 'sonner';
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
} from './ui/dialog';

type SortKey = 'name' | 'size' | 'date';
type Clipboard = { op: 'copy' | 'move'; paths: string[] };
type FileCategory = 'text' | 'image' | 'disk' | 'binary';

const TEXT_EXTS = new Set(['txt', 'cfg', 'ini', 'json', 'bas', 'asm', 'seq', 'rel', 'prg', 'md', 'log', 'csv']);
const IMAGE_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'svg', 'webp']);
const DISK_EXTS = new Set(['d64', 'd71', 'd81', 'd82', 'g64', 'g71', 't64', 'tap', 'crt', 'nib']);

function fileCategory(entry: EntryInfo): FileCategory {
  if (entry.type === 'folder') return 'binary';
  const ext = entry.name.split('.').pop()?.toLowerCase() ?? '';
  if (TEXT_EXTS.has(ext)) return 'text';
  if (IMAGE_EXTS.has(ext)) return 'image';
  if (DISK_EXTS.has(ext)) return 'disk';
  return 'binary';
}

function EntryIcon({ entry }: { entry: EntryInfo }) {
  if (entry.type === 'folder') return <Folder className="w-5 h-5 text-blue-500 flex-shrink-0" />;
  const cat = fileCategory(entry);
  if (cat === 'image') return <ImageIcon className="w-5 h-5 text-purple-500 flex-shrink-0" />;
  if (cat === 'text') return <FileText className="w-5 h-5 text-green-600 flex-shrink-0" />;
  if (cat === 'disk') return <HardDrive className="w-5 h-5 text-amber-500 flex-shrink-0" />;
  return <File className="w-5 h-5 text-neutral-400 flex-shrink-0" />;
}

interface FileManagerProps {
  initialPath?: string;
  config?: any;
  setConfig?: (c: any) => void;
  onBack?: () => void;
}

export default function FileManager({ initialPath = '/', config, setConfig, onBack }: FileManagerProps) {
  const [path, setPath] = useState(normalizePath(initialPath));
  const [entries, setEntries] = useState<EntryInfo[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [filter, setFilter] = useState('');
  const [sortKey, setSortKey] = useState<SortKey>('name');
  const [sortAsc, setSortAsc] = useState(true);
  const [clipboard, setClipboard] = useState<Clipboard | null>(null);
  const [actionEntry, setActionEntry] = useState<EntryInfo | null>(null);
  const [dragOver, setDragOver] = useState(false);
  const [viewEntry, setViewEntry] = useState<EntryInfo | null>(null);
  const [viewText, setViewText] = useState<string | null>(null);
  const [viewImgUrl, setViewImgUrl] = useState<string | null>(null);
  const [viewLoading, setViewLoading] = useState(false);
  const [showNewFolder, setShowNewFolder] = useState(false);
  const [newFolderName, setNewFolderName] = useState('');
  const [renameEntry, setRenameEntry] = useState<EntryInfo | null>(null);
  const [renameName, setRenameName] = useState('');
  const [mountEntry, setMountEntry] = useState<EntryInfo | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const dragCounter = useRef(0);

  const load = useCallback(async (p: string) => {
    setLoading(true);
    setError(null);
    setSelected(new Set());
    try {
      const items = await listDirectory(p);
      setEntries(items);
    } catch (e: any) {
      setError(e?.message || 'Failed to load directory');
      setEntries([]);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    void load(path);
  }, [path, load]);

  const navigateTo = (p: string) => {
    setPath(normalizePath(p));
    setFilter('');
  };

  const navigateUp = () => {
    if (path === '/') return;
    navigateTo(splitPath(path).parent);
  };

  // Sorted + filtered view
  const visible = entries
    .filter(e => !filter || e.name.toLowerCase().includes(filter.toLowerCase()))
    .sort((a, b) => {
      if (a.type !== b.type) return a.type === 'folder' ? -1 : 1;
      let cmp = 0;
      if (sortKey === 'name') cmp = a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
      else if (sortKey === 'size') cmp = a.size - b.size;
      else if (sortKey === 'date') cmp = (a.lastModified?.getTime() ?? 0) - (b.lastModified?.getTime() ?? 0);
      return sortAsc ? cmp : -cmp;
    });

  const toggleSort = (key: SortKey) => {
    if (sortKey === key) setSortAsc(v => !v);
    else { setSortKey(key); setSortAsc(true); }
  };

  // Multi-select
  const toggleSelect = (entryPath: string) => {
    setSelected(prev => {
      const next = new Set(prev);
      if (next.has(entryPath)) next.delete(entryPath);
      else next.add(entryPath);
      return next;
    });
  };

  const selectAll = () => {
    setSelected(selected.size === visible.length && visible.length > 0 ? new Set() : new Set(visible.map(e => e.path)));
  };

  // File viewer
  const openEntry = async (entry: EntryInfo) => {
    if (renameEntry !== null) return;
    if (entry.type === 'folder') {
      navigateTo(joinPath(path, entry.name));
      return;
    }
    const cat = fileCategory(entry);
    if (cat === 'disk') {
      setMountEntry(entry);
      return;
    }
    if (cat === 'binary') {
      void downloadEntry(entry);
      return;
    }
    setViewEntry(entry);
    setViewText(null);
    setViewImgUrl(null);
    setViewLoading(true);
    try {
      const blob = await getFileContents(entry.path);
      if (cat === 'image') {
        setViewImgUrl(URL.createObjectURL(blob));
      } else {
        setViewText(await blob.text());
      }
    } catch (e: any) {
      toast.error(`Failed to open ${entry.name}: ${e?.message || e}`);
      setViewEntry(null);
    } finally {
      setViewLoading(false);
    }
  };

  const closeViewer = () => {
    if (viewImgUrl) URL.revokeObjectURL(viewImgUrl);
    setViewEntry(null);
    setViewText(null);
    setViewImgUrl(null);
  };

  // Download
  const triggerDownload = (blob: Blob, name: string) => {
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = name;
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
  };

  const downloadEntry = async (entry: EntryInfo) => {
    try {
      const blob = await getFileContents(entry.path);
      triggerDownload(blob, entry.name);
    } catch (e: any) {
      toast.error(`Download failed: ${e?.message || e}`);
    }
  };

  const downloadSelected = async () => {
    const targets = entries.filter(e => selected.has(e.path) && e.type === 'file');
    for (const entry of targets) await downloadEntry(entry);
  };

  // Delete
  const deleteEntry = async (entry: EntryInfo) => {
    const ok = window.confirm(
      entry.type === 'folder'
        ? `Delete folder "${entry.name}" and all its contents?`
        : `Delete "${entry.name}"?`,
    );
    if (!ok) return;
    try {
      await deletePath(entry.path);
      toast.success(`Deleted ${entry.name}`);
      void load(path);
    } catch (e: any) {
      toast.error(`Delete failed: ${e?.message || e}`);
    }
  };

  const deleteSelected = async () => {
    const targets = entries.filter(e => selected.has(e.path));
    const ok = window.confirm(`Delete ${targets.length} item${targets.length !== 1 ? 's' : ''}?`);
    if (!ok) return;
    let failed = 0;
    for (const entry of targets) {
      try {
        await deletePath(entry.path);
      } catch {
        failed++;
      }
    }
    if (failed) toast.error(`${failed} item${failed !== 1 ? 's' : ''} failed to delete`);
    else toast.success(`Deleted ${targets.length} item${targets.length !== 1 ? 's' : ''}`);
    void load(path);
    setSelected(new Set());
  };

  // Rename
  const startRename = (entry: EntryInfo) => {
    setRenameEntry(entry);
    setRenameName(entry.name);
    setActionEntry(null);
  };

  const commitRename = async () => {
    if (!renameEntry) return;
    const newName = renameName.trim();
    if (!newName || newName === renameEntry.name) {
      setRenameEntry(null);
      return;
    }
    const dest = joinPath(splitPath(renameEntry.path).parent, newName);
    try {
      await movePath(renameEntry.path, dest);
      toast.success(`Renamed to "${newName}"`);
      void load(path);
    } catch (e: any) {
      toast.error(`Rename failed: ${e?.message || e}`);
    } finally {
      setRenameEntry(null);
    }
  };

  // Mount disk image onto a drive device
  const mountOnDevice = (deviceType: 'drive' | 'meatloaf', key: string) => {
    if (!mountEntry || !setConfig || !config) return;
    const url = `${getWebDAVBaseUrl()}${mountEntry.path}`;
    const newConfig = JSON.parse(JSON.stringify(config));
    if (!newConfig.iec) newConfig.iec = {};
    if (!newConfig.iec.devices) newConfig.iec.devices = {};
    if (!newConfig.iec.devices[deviceType]) newConfig.iec.devices[deviceType] = {};
    if (!newConfig.iec.devices[deviceType][key]) newConfig.iec.devices[deviceType][key] = {};
    newConfig.iec.devices[deviceType][key].url = url;
    setConfig(newConfig);
    toast.success(`Mounted "${mountEntry.name}" on device #${key}`);
    setMountEntry(null);
  };

  // Clipboard (copy / move)
  const cutOrCopyEntry = (entry: EntryInfo, op: 'copy' | 'move') => {
    setClipboard({ op, paths: [entry.path] });
    toast.success(`"${entry.name}" ${op === 'copy' ? 'copied' : 'cut'} — navigate and paste`);
    setActionEntry(null);
  };

  const copySelected = () => {
    setClipboard({ op: 'copy', paths: [...selected] });
    toast.success(`${selected.size} item${selected.size !== 1 ? 's' : ''} copied — navigate and paste`);
    setSelected(new Set());
  };

  const moveSelected = () => {
    setClipboard({ op: 'move', paths: [...selected] });
    toast.success(`${selected.size} item${selected.size !== 1 ? 's' : ''} cut — navigate and paste`);
    setSelected(new Set());
  };

  const paste = async () => {
    if (!clipboard) return;
    let failed = 0;
    for (const src of clipboard.paths) {
      const name = splitPath(src).name;
      const dest = joinPath(path, name);
      try {
        if (clipboard.op === 'copy') await copyPath(src, dest);
        else await movePath(src, dest);
      } catch {
        failed++;
      }
    }
    if (failed) toast.error(`${failed} item${failed !== 1 ? 's' : ''} failed`);
    else toast.success(`Pasted ${clipboard.paths.length} item${clipboard.paths.length !== 1 ? 's' : ''}`);
    setClipboard(null);
    void load(path);
  };

  // New folder
  const handleCreateFolder = async () => {
    const name = newFolderName.trim();
    if (!name) return;
    try {
      await createFolder(joinPath(path, name), true);
      toast.success(`Created folder "${name}"`);
      setShowNewFolder(false);
      setNewFolderName('');
      void load(path);
    } catch (e: any) {
      toast.error(`Failed to create folder: ${e?.message || e}`);
    }
  };

  // Upload
  const handleUpload = async (file: File) => {
    const target = joinPath(path, file.name);
    try {
      await putFileContents(target, await file.arrayBuffer());
      toast.success(`Uploaded ${file.name}`);
      void load(path);
    } catch (e: any) {
      toast.error(`Upload failed for ${file.name}: ${e?.message || e}`);
    }
  };

  const onPickFiles = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (!e.target.files) return;
    Array.from(e.target.files).forEach(f => void handleUpload(f));
    e.target.value = '';
  };

  // Drag & drop
  const onDragEnter = (e: React.DragEvent) => {
    e.preventDefault();
    dragCounter.current++;
    if (e.dataTransfer.types.includes('Files')) setDragOver(true);
  };

  const onDragLeave = () => {
    dragCounter.current--;
    if (dragCounter.current <= 0) { dragCounter.current = 0; setDragOver(false); }
  };

  const onDragOver = (e: React.DragEvent) => e.preventDefault();

  const onDrop = (e: React.DragEvent) => {
    e.preventDefault();
    dragCounter.current = 0;
    setDragOver(false);
    Array.from(e.dataTransfer.files).forEach(f => void handleUpload(f));
  };

  const pathParts = path.split('/').filter(Boolean);
  const selCount = selected.size;

  return (
    <div
      className="relative flex flex-col h-full"
      onDragEnter={onDragEnter}
      onDragLeave={onDragLeave}
      onDragOver={onDragOver}
      onDrop={onDrop}
    >
      {/* Header */}
      <div className="bg-white border-b border-neutral-200 px-4 py-3 flex-shrink-0">
        <div className="flex items-center gap-2 mb-2">
          {onBack && (
            <button onClick={onBack} className="p-1 rounded hover:bg-neutral-100" title="Back">
              <ChevronLeft className="w-5 h-5" />
            </button>
          )}
          <h2 className="font-semibold flex-1 text-sm">File Manager</h2>
          <button onClick={() => void load(path)} className="p-1.5 rounded hover:bg-neutral-100" title="Refresh">
            <RefreshCw className="w-4 h-4" />
          </button>
          <button onClick={() => setShowNewFolder(v => !v)} className="p-1.5 rounded hover:bg-neutral-100" title="New Folder">
            <FolderPlus className="w-4 h-4" />
          </button>
          <button onClick={() => fileInputRef.current?.click()} className="p-1.5 rounded hover:bg-neutral-100" title="Upload">
            <Upload className="w-4 h-4" />
          </button>
          <input ref={fileInputRef} type="file" multiple className="hidden" onChange={onPickFiles} />
        </div>

        {/* Breadcrumb */}
        <div className="flex items-center gap-1 text-sm text-neutral-600 overflow-x-auto">
          <button onClick={() => navigateTo('/')} className="p-1 rounded hover:bg-neutral-100 flex-shrink-0" title="Root">
            <Home className="w-4 h-4" />
          </button>
          {pathParts.map((part, i) => (
            <div key={i} className="flex items-center gap-1 flex-shrink-0">
              <ChevronRight className="w-3 h-3 text-neutral-400" />
              <button
                onClick={() => navigateTo('/' + pathParts.slice(0, i + 1).join('/'))}
                className="hover:text-blue-600 hover:underline max-w-[120px] truncate"
              >
                {part}
              </button>
            </div>
          ))}
        </div>

        {showNewFolder && (
          <div className="mt-2 flex items-center gap-2">
            <input
              value={newFolderName}
              onChange={e => setNewFolderName(e.target.value)}
              onKeyDown={e => {
                if (e.key === 'Enter') void handleCreateFolder();
                if (e.key === 'Escape') { setShowNewFolder(false); setNewFolderName(''); }
              }}
              placeholder="New folder name"
              className="flex-1 px-2 py-1 text-sm border border-neutral-300 rounded"
              autoFocus
            />
            <button onClick={() => void handleCreateFolder()} className="px-3 py-1 text-sm bg-blue-600 text-white rounded">
              Create
            </button>
          </div>
        )}
      </div>

      {/* Filter + sort bar */}
      <div className="bg-neutral-50 border-b border-neutral-200 px-4 py-2 flex items-center gap-2 flex-shrink-0">
        <div className="relative flex-1 min-w-0">
          <Search className="absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-neutral-400 pointer-events-none" />
          <input
            value={filter}
            onChange={e => setFilter(e.target.value)}
            placeholder="Filter…"
            className="w-full pl-7 pr-6 py-1 text-sm border border-neutral-300 rounded bg-white"
          />
          {filter && (
            <button onClick={() => setFilter('')} className="absolute right-2 top-1/2 -translate-y-1/2">
              <X className="w-3.5 h-3.5 text-neutral-400" />
            </button>
          )}
        </div>
        {(['name', 'size', 'date'] as SortKey[]).map(k => (
          <button
            key={k}
            onClick={() => toggleSort(k)}
            className={`text-xs px-2 py-1 rounded border flex-shrink-0 ${sortKey === k ? 'border-blue-400 bg-blue-50 text-blue-700' : 'border-neutral-300 bg-white text-neutral-600'}`}
          >
            {k === 'name' ? 'Name' : k === 'size' ? 'Size' : 'Date'}
            {sortKey === k ? (sortAsc ? ' ↑' : ' ↓') : ''}
          </button>
        ))}
      </div>

      {/* Selection / clipboard bar */}
      {(selCount > 0 || clipboard) && (
        <div className="bg-blue-50 border-b border-blue-200 px-3 py-2 flex items-center gap-2 flex-shrink-0 flex-wrap text-sm">
          {selCount > 0 && (
            <>
              <span className="text-blue-700 font-medium">{selCount} selected</span>
              <button onClick={() => void downloadSelected()} className="px-2 py-0.5 rounded border border-blue-300 bg-white text-blue-700 hover:bg-blue-100 inline-flex items-center gap-1">
                <Download className="w-3.5 h-3.5" /> Download
              </button>
              <button onClick={copySelected} className="px-2 py-0.5 rounded border border-blue-300 bg-white text-blue-700 hover:bg-blue-100 inline-flex items-center gap-1">
                <Copy className="w-3.5 h-3.5" /> Copy
              </button>
              <button onClick={moveSelected} className="px-2 py-0.5 rounded border border-blue-300 bg-white text-blue-700 hover:bg-blue-100 inline-flex items-center gap-1">
                <Move className="w-3.5 h-3.5" /> Move
              </button>
              <button onClick={() => void deleteSelected()} className="px-2 py-0.5 rounded border border-red-300 bg-white text-red-700 hover:bg-red-50 inline-flex items-center gap-1">
                <Trash2 className="w-3.5 h-3.5" /> Delete
              </button>
              <button onClick={() => setSelected(new Set())} className="ml-auto p-1 rounded hover:bg-blue-200">
                <X className="w-3.5 h-3.5 text-blue-600" />
              </button>
            </>
          )}
          {selCount === 0 && clipboard && (
            <>
              <span className="text-blue-700 font-medium">
                {clipboard.op === 'copy' ? 'Copy' : 'Move'} {clipboard.paths.length} item{clipboard.paths.length !== 1 ? 's' : ''} here?
              </span>
              <button onClick={() => void paste()} className="px-3 py-0.5 rounded bg-blue-600 text-white hover:bg-blue-700 inline-flex items-center gap-1">
                <Check className="w-3.5 h-3.5" /> Paste
              </button>
              <button onClick={() => setClipboard(null)} className="ml-auto p-1 rounded hover:bg-blue-200">
                <X className="w-3.5 h-3.5 text-blue-600" />
              </button>
            </>
          )}
        </div>
      )}

      {/* File list */}
      <div className="flex-1 overflow-y-auto">
        {loading && (
          <div className="p-8 flex flex-col items-center gap-2 text-neutral-500 text-sm">
            <Loader2 className="w-6 h-6 animate-spin" />
            Loading…
          </div>
        )}

        {!loading && error && (
          <div className="p-4 text-sm">
            <div className="text-red-600 mb-1">Failed to load directory</div>
            <div className="text-neutral-500 text-xs break-all">{error}</div>
            <button onClick={() => void load(path)} className="mt-3 text-blue-600 inline-flex items-center gap-1 text-sm">
              <RefreshCw className="w-3 h-3" /> Retry
            </button>
          </div>
        )}

        {!loading && !error && (
          <>
            {path !== '/' && (
              <button
                onClick={navigateUp}
                className="w-full px-4 py-3 flex items-center gap-3 hover:bg-neutral-50 border-b border-neutral-100 text-left"
              >
                <div className="w-4 flex-shrink-0" />
                <ArrowLeft className="w-5 h-5 text-neutral-400 flex-shrink-0" />
                <span className="text-neutral-600">..</span>
              </button>
            )}

            {visible.length > 0 && (
              <div className="px-4 py-1.5 flex items-center gap-3 border-b border-neutral-100 bg-neutral-50 text-xs text-neutral-500">
                <input
                  type="checkbox"
                  checked={selCount === visible.length && visible.length > 0}
                  onChange={selectAll}
                  className="w-4 h-4"
                />
                <span>{visible.length} item{visible.length !== 1 ? 's' : ''}{filter ? ' (filtered)' : ''}</span>
              </div>
            )}

            {visible.map(entry => (
              <div
                key={entry.path}
                className={`px-4 py-3 flex items-center gap-3 border-b border-neutral-100 hover:bg-neutral-50 ${selected.has(entry.path) ? 'bg-blue-50' : ''}`}
              >
                <input
                  type="checkbox"
                  checked={selected.has(entry.path)}
                  onChange={() => toggleSelect(entry.path)}
                  onClick={e => e.stopPropagation()}
                  className="w-4 h-4 flex-shrink-0"
                />
                <button
                  onClick={() => void openEntry(entry)}
                  className="flex-1 flex items-center gap-3 text-left min-w-0"
                >
                  <EntryIcon entry={entry} />
                  <div className="min-w-0 flex-1">
                    {renameEntry?.path === entry.path ? (
                      <input
                        value={renameName}
                        onChange={e => setRenameName(e.target.value)}
                        onKeyDown={e => {
                          if (e.key === 'Enter') void commitRename();
                          if (e.key === 'Escape') setRenameEntry(null);
                        }}
                        onBlur={() => void commitRename()}
                        onClick={e => e.stopPropagation()}
                        onFocus={e => {
                          const dotIdx = renameName.lastIndexOf('.');
                          const end = dotIdx > 0 ? dotIdx : renameName.length;
                          e.currentTarget.setSelectionRange(0, end);
                        }}
                        className="w-full px-1 py-0 border border-blue-400 rounded text-sm"
                        autoFocus
                      />
                    ) : (
                      <div className="text-neutral-900 truncate text-sm">{entry.name}</div>
                    )}
                    {entry.type === 'file' && (
                      <div className="text-xs text-neutral-400 truncate">
                        {humanFileSize(entry.size)}
                        {entry.lastModified ? ` · ${entry.lastModified.toLocaleDateString()}` : ''}
                      </div>
                    )}
                  </div>
                  {entry.type === 'folder' && <ChevronRight className="w-4 h-4 text-neutral-400 flex-shrink-0" />}
                </button>
                <button
                  onClick={e => { e.stopPropagation(); if (renameEntry !== null) return; setActionEntry(entry); }}
                  className="p-2 rounded hover:bg-neutral-200 flex-shrink-0"
                  title="Actions"
                >
                  <MoreVertical className="w-4 h-4" />
                </button>
              </div>
            ))}

            {visible.length === 0 && entries.length === 0 && (
              <div className="p-8 text-center text-neutral-500 text-sm">
                Empty folder — drop files here to upload
              </div>
            )}
            {visible.length === 0 && entries.length > 0 && (
              <div className="p-8 text-center text-neutral-500 text-sm">No files match the filter</div>
            )}
          </>
        )}
      </div>

      {/* Drag overlay */}
      {dragOver && (
        <div className="absolute inset-0 bg-blue-200/60 flex items-center justify-center pointer-events-none z-40 rounded">
          <div className="bg-white rounded-xl shadow-lg px-8 py-6 flex flex-col items-center gap-3 text-blue-700">
            <Upload className="w-10 h-10" />
            <span className="font-medium">Drop to upload to {path}</span>
          </div>
        </div>
      )}

      {/* Per-entry action menu */}
      <Dialog open={actionEntry !== null} onOpenChange={open => !open && setActionEntry(null)}>
        <DialogContent className="max-w-sm">
          <DialogHeader>
            <DialogTitle className="truncate">{actionEntry?.name}</DialogTitle>
            <DialogDescription>
              {actionEntry?.type === 'folder' ? 'Folder' : humanFileSize(actionEntry?.size ?? 0)}
            </DialogDescription>
          </DialogHeader>
          <div className="flex flex-col gap-2">
            <button
              onClick={() => { if (actionEntry) void openEntry(actionEntry); setActionEntry(null); }}
              className="w-full text-left px-4 py-3 rounded border border-neutral-200 hover:bg-blue-50 hover:border-blue-300 inline-flex items-center gap-3"
            >
              {actionEntry?.type === 'folder'
                ? <Folder className="w-4 h-4 text-blue-600" />
                : <Eye className="w-4 h-4 text-blue-600" />}
              <span>{actionEntry?.type === 'folder' ? 'Open folder' : 'Open / View'}</span>
            </button>
            {actionEntry?.type === 'file' && (
              <button
                onClick={() => { if (actionEntry) void downloadEntry(actionEntry); setActionEntry(null); }}
                className="w-full text-left px-4 py-3 rounded border border-neutral-200 hover:bg-neutral-50 inline-flex items-center gap-3"
              >
                <Download className="w-4 h-4" /> <span>Download</span>
              </button>
            )}
            {actionEntry?.type === 'file' && fileCategory(actionEntry) === 'disk' && (
              <button
                onClick={() => { if (actionEntry) setMountEntry(actionEntry); setActionEntry(null); }}
                className="w-full text-left px-4 py-3 rounded border border-neutral-200 hover:bg-neutral-50 inline-flex items-center gap-3"
              >
                <HardDrive className="w-4 h-4 text-amber-600" /> <span>Mount on virtual drive</span>
              </button>
            )}
            <button
              onClick={() => { if (actionEntry) startRename(actionEntry); }}
              className="w-full text-left px-4 py-3 rounded border border-neutral-200 hover:bg-neutral-50 inline-flex items-center gap-3"
            >
              <Pencil className="w-4 h-4" /> <span>Rename</span>
            </button>
            <button
              onClick={() => { if (actionEntry) cutOrCopyEntry(actionEntry, 'copy'); }}
              className="w-full text-left px-4 py-3 rounded border border-neutral-200 hover:bg-neutral-50 inline-flex items-center gap-3"
            >
              <Copy className="w-4 h-4" /> <span>Copy</span>
            </button>
            <button
              onClick={() => { if (actionEntry) cutOrCopyEntry(actionEntry, 'move'); }}
              className="w-full text-left px-4 py-3 rounded border border-neutral-200 hover:bg-neutral-50 inline-flex items-center gap-3"
            >
              <Move className="w-4 h-4" /> <span>Move (Cut)</span>
            </button>
            <button
              onClick={() => { if (actionEntry) void deleteEntry(actionEntry); setActionEntry(null); }}
              className="w-full text-left px-4 py-3 rounded border border-red-200 hover:bg-red-50 text-red-700 inline-flex items-center gap-3"
            >
              <Trash2 className="w-4 h-4" /> <span>Delete</span>
            </button>
          </div>
        </DialogContent>
      </Dialog>

      {/* File viewer overlay */}
      {viewEntry && (
        <div className="fixed inset-0 bg-black/80 z-50 flex flex-col">
          <div className="bg-white flex items-center px-4 py-3 gap-3 shadow flex-shrink-0">
            <button onClick={closeViewer} className="p-1 rounded hover:bg-neutral-100">
              <X className="w-5 h-5" />
            </button>
            <span className="font-medium truncate flex-1 text-sm">{viewEntry.name}</span>
            <button onClick={() => void downloadEntry(viewEntry)} className="p-1.5 rounded hover:bg-neutral-100" title="Download">
              <Download className="w-4 h-4" />
            </button>
          </div>
          <div className="flex-1 overflow-auto flex items-center justify-center bg-neutral-900">
            {viewLoading && (
              <div className="flex items-center gap-2 text-neutral-400">
                <Loader2 className="w-5 h-5 animate-spin" /> Loading…
              </div>
            )}
            {!viewLoading && viewImgUrl && (
              <img src={viewImgUrl} alt={viewEntry.name} className="max-w-full max-h-full object-contain" />
            )}
            {!viewLoading && viewText !== null && (
              <pre className="text-green-400 text-xs font-mono p-4 whitespace-pre-wrap break-all w-full h-full overflow-auto">{viewText}</pre>
            )}
          </div>
        </div>
      )}

      {/* Mount dialog */}
      <Dialog open={mountEntry !== null} onOpenChange={open => !open && setMountEntry(null)}>
        <DialogContent className="max-w-sm">
          <DialogHeader>
            <DialogTitle>Mount on Virtual Drive</DialogTitle>
            <DialogDescription className="truncate">{mountEntry?.name}</DialogDescription>
          </DialogHeader>
          {(() => {
            const driveDevices = Object.entries(config?.iec?.devices?.drive ?? {})
              .filter(([key]) => key !== 'vdrive' && key !== 'rom')
              .map(([key, value]: [string, any]) => ({ deviceType: 'drive' as const, key, url: value?.url as string | undefined, enabled: !!value?.enabled }));
            const meatloafDevices = Object.entries(config?.iec?.devices?.meatloaf ?? {})
              .map(([key, value]: [string, any]) => ({ deviceType: 'meatloaf' as const, key, url: value?.url as string | undefined, enabled: !!value?.enabled }));
            const devices = [...driveDevices, ...meatloafDevices];
            if (devices.length === 0) {
              return <p className="text-sm text-neutral-500 text-center py-4">No drive devices found in config.</p>;
            }
            return (
              <div className="flex flex-col gap-2">
                {devices.map(dev => (
                  <button
                    key={`${dev.deviceType}-${dev.key}`}
                    onClick={() => mountOnDevice(dev.deviceType, dev.key)}
                    className="w-full text-left px-4 py-3 rounded border border-neutral-200 hover:bg-blue-50 hover:border-blue-300 inline-flex items-center gap-3"
                  >
                    <HardDrive className={`w-5 h-5 flex-shrink-0 ${dev.enabled ? 'text-blue-500' : 'text-neutral-400'}`} />
                    <div className="min-w-0 flex-1">
                      <div className="font-medium text-sm">Device #{dev.key}</div>
                      {dev.url && <div className="text-xs text-neutral-500 truncate">{dev.url}</div>}
                    </div>
                    {!dev.enabled && <span className="text-xs text-neutral-400 flex-shrink-0">disabled</span>}
                  </button>
                ))}
              </div>
            );
          })()}
        </DialogContent>
      </Dialog>
    </div>
  );
}