import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
import {
    Folder, File, ArrowUp, List, LayoutGrid,
    ArrowUpDown, Clock, FileType, Type,
    ChevronRight, Info, Loader2, Download, ExternalLink, X, ChevronLeft, ZoomIn, ZoomOut,
    Image, Film, Music, FileCode, FileText as FileTextIcon,
    Archive, FileSpreadsheet, Presentation
} from 'lucide-react';
import type { FileBrowserWidgetProps } from '@polymech/shared';
import { useAuth } from '@/hooks/useAuth';

// ── Types ────────────────────────────────────────────────────────

interface INode {
    name: string;
    path: string;
    size: number;
    mtime?: number;
    mime?: string;
    parent: string;
    type: string;
}

type SortKey = 'name' | 'ext' | 'date' | 'type';
type MimeCategory = 'dir' | 'image' | 'video' | 'audio' | 'code' | 'document' | 'archive' | 'spreadsheet' | 'presentation' | 'other';

// ── MIME helpers ─────────────────────────────────────────────────

const IMAGE_EXTS = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'avif', 'bmp', 'ico', 'tiff', 'tif']);
const VIDEO_EXTS = new Set(['mp4', 'mov', 'webm', 'mkv', 'avi', 'flv', 'wmv', 'm4v', 'ogv']);
const AUDIO_EXTS = new Set(['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma', 'opus']);
const CODE_EXTS = new Set(['ts', 'tsx', 'js', 'jsx', 'py', 'rb', 'go', 'rs', 'java', 'c', 'cpp', 'h', 'hpp', 'cs', 'swift', 'kt', 'sh', 'bash', 'zsh', 'ps1', 'bat', 'cmd', 'lua', 'php', 'sql', 'r', 'html', 'htm', 'css', 'scss', 'sass', 'less', 'json', 'yaml', 'yml', 'toml', 'xml', 'vue', 'svelte']);
const DOC_EXTS = new Set(['md', 'txt', 'rtf', 'pdf', 'doc', 'docx', 'odt', 'tex', 'log']);
const ARCHIVE_EXTS = new Set(['zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'zst', 'tgz']);
const SPREADSHEET_EXTS = new Set(['xls', 'xlsx', 'csv', 'ods', 'tsv']);
const PRESENTATION_EXTS = new Set(['ppt', 'pptx', 'odp', 'key']);

function getExt(name: string): string {
    const i = name.lastIndexOf('.');
    return i > 0 ? name.slice(i + 1).toLowerCase() : '';
}

function getMimeCategory(node: INode): MimeCategory {
    if (node.mime === 'inode/directory' || node.type === 'dir') return 'dir';
    const mime = node.mime || '';
    if (mime.startsWith('image/')) return 'image';
    if (mime.startsWith('video/')) return 'video';
    if (mime.startsWith('audio/')) return 'audio';
    if (mime.startsWith('text/x-') || mime.includes('javascript') || mime.includes('typescript') || mime.includes('json') || mime.includes('xml') || mime.includes('yaml')) return 'code';
    const ext = getExt(node.name);
    if (IMAGE_EXTS.has(ext)) return 'image';
    if (VIDEO_EXTS.has(ext)) return 'video';
    if (AUDIO_EXTS.has(ext)) return 'audio';
    if (CODE_EXTS.has(ext)) return 'code';
    if (SPREADSHEET_EXTS.has(ext)) return 'spreadsheet';
    if (PRESENTATION_EXTS.has(ext)) return 'presentation';
    if (ARCHIVE_EXTS.has(ext)) return 'archive';
    if (DOC_EXTS.has(ext)) return 'document';
    return 'other';
}

const CATEGORY_STYLE: Record<MimeCategory, { icon: React.FC<any>; color: string }> = {
    dir: { icon: Folder, color: '#60a5fa' },
    image: { icon: Image, color: '#22c55e' },
    video: { icon: Film, color: '#ef4444' },
    audio: { icon: Music, color: '#8b5cf6' },
    code: { icon: FileCode, color: '#3b82f6' },
    document: { icon: FileTextIcon, color: '#f59e0b' },
    archive: { icon: Archive, color: '#eab308' },
    spreadsheet: { icon: FileSpreadsheet, color: '#16a34a' },
    presentation: { icon: Presentation, color: '#ec4899' },
    other: { icon: File, color: '#94a3b8' },
};

function NodeIcon({ node, size = 14 }: { node: INode; size?: number }) {
    const cat = getMimeCategory(node);
    const { icon: Icon, color } = CATEGORY_STYLE[cat];
    return <Icon size={size} style={{ color, flexShrink: 0 }} />;
}

// ── Format helpers ───────────────────────────────────────────────

function formatSize(bytes: number): string {
    if (bytes === 0) return '—';
    const units = ['B', 'KB', 'MB', 'GB'];
    const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
    const v = bytes / Math.pow(1024, i);
    return `${v < 10 ? v.toFixed(1) : Math.round(v)} ${units[i]}`;
}

function formatDate(ts?: number): string {
    if (!ts) return '—';
    const d = new Date(ts);
    return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })
        + ' ' + d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
}

// ── Sort ─────────────────────────────────────────────────────────

function sortNodes(nodes: INode[], sortBy: SortKey, asc: boolean): INode[] {
    const dirs = nodes.filter(n => n.mime === 'inode/directory' || n.type === 'dir');
    const files = nodes.filter(n => n.mime !== 'inode/directory' && n.type !== 'dir');
    const cmp = (a: INode, b: INode): number => {
        let r = 0;
        switch (sortBy) {
            case 'name': r = a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }); break;
            case 'ext': r = getExt(a.name).localeCompare(getExt(b.name)) || a.name.localeCompare(b.name); break;
            case 'date': r = (a.mtime ?? 0) - (b.mtime ?? 0); break;
            case 'type': r = getMimeCategory(a).localeCompare(getMimeCategory(b)) || a.name.localeCompare(b.name); break;
        }
        return asc ? r : -r;
    };
    dirs.sort(cmp);
    files.sort(cmp);
    return [...dirs, ...files];
}

// ── Thumbnail helper ─────────────────────────────────────────────

function ThumbPreview({ node, mount, height = 64, tokenParam = '' }: { node: INode; mount: string; height?: number; tokenParam?: string }) {
    const cat = getMimeCategory(node);
    const baseUrl = `/api/vfs/get/${encodeURIComponent(mount)}/${node.path}`;
    const fileUrl = tokenParam ? `${baseUrl}?${tokenParam}` : baseUrl;
    if (cat === 'image') {
        return <img src={fileUrl} alt={node.name} loading="lazy" style={{ width: '100%', height, objectFit: 'cover', borderRadius: 4 }} />;
    }
    if (cat === 'video') {
        return (
            <div style={{ position: 'relative', width: '100%', height }}>
                <video src={fileUrl} muted preload="metadata" style={{ width: '100%', height, objectFit: 'cover', borderRadius: 4 }} />
                <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.3)', borderRadius: 4 }}>
                    <Film size={20} style={{ color: '#fff', opacity: 0.8 }} />
                </div>
            </div>
        );
    }
    return <NodeIcon node={node} size={28} />;
}

// ── Toolbar button ───────────────────────────────────────────────

const TB_BTN: React.CSSProperties = {
    background: 'none', border: 'none', cursor: 'pointer',
    padding: 4, display: 'flex', alignItems: 'center', justifyContent: 'center',
    color: 'var(--muted-foreground, #94a3b8)', borderRadius: 4,
};
const TB_BTN_ACTIVE: React.CSSProperties = { ...TB_BTN, color: 'var(--foreground, #e2e8f0)' };
const TB_SEP: React.CSSProperties = { width: 1, height: 18, background: 'var(--border, #334155)', flexShrink: 0 };

// ── Main Component ───────────────────────────────────────────────

const FileBrowserWidget: React.FC<FileBrowserWidgetProps & { variables?: any }> = (props) => {
    const {
        mount = 'root',
        path: initialPath = '/',
        glob = '*.*',
        mode = 'simple',
        viewMode: initialViewMode = 'list',
        sortBy: initialSort = 'name',
        showToolbar = true,
    } = props;

    const { session } = useAuth();
    const accessToken = session?.access_token;

    const [currentPath, setCurrentPath] = useState(initialPath);
    const [nodes, setNodes] = useState<INode[]>([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const [viewMode, setViewMode] = useState<'list' | 'thumbs'>(initialViewMode);
    const [sortBy, setSortBy] = useState<SortKey>(initialSort);
    const [sortAsc, setSortAsc] = useState(true);
    const [focusIdx, setFocusIdx] = useState(-1);
    const [selected, setSelected] = useState<INode | null>(null);
    const listRef = useRef<HTMLDivElement>(null);
    const containerRef = useRef<HTMLDivElement>(null);

    // ── Zoom (persisted) ─────────────────────────────────────────
    const [thumbSize, setThumbSize] = useState(() => {
        const v = localStorage.getItem('fb-thumb-size');
        return v ? Math.max(60, Math.min(200, Number(v))) : 100;
    });
    const [fontSize, setFontSize] = useState(() => {
        const v = localStorage.getItem('fb-font-size');
        return v ? Math.max(10, Math.min(18, Number(v))) : 14;
    });
    const zoomIn = () => {
        if (viewMode === 'thumbs') setThumbSize(s => { const n = Math.min(200, s + 20); localStorage.setItem('fb-thumb-size', String(n)); return n; });
        else setFontSize(s => { const n = Math.min(18, s + 1); localStorage.setItem('fb-font-size', String(n)); return n; });
    };
    const zoomOut = () => {
        if (viewMode === 'thumbs') setThumbSize(s => { const n = Math.max(60, s - 20); localStorage.setItem('fb-thumb-size', String(n)); return n; });
        else setFontSize(s => { const n = Math.max(10, s - 1); localStorage.setItem('fb-font-size', String(n)); return n; });
    };

    // ── Fetch ───────────────────────────────────────────────────

    const fetchDir = useCallback(async (dirPath: string) => {
        setLoading(true);
        setError(null);
        setSelected(null);
        setFocusIdx(-1);
        try {
            const clean = dirPath.replace(/^\/+/, '');
            const base = clean
                ? `/api/vfs/ls/${encodeURIComponent(mount)}/${clean}`
                : `/api/vfs/ls/${encodeURIComponent(mount)}`;
            const url = glob && glob !== '*.*' ? `${base}?glob=${encodeURIComponent(glob)}` : base;
            const headers: Record<string, string> = {};
            if (accessToken) headers['Authorization'] = `Bearer ${accessToken}`;
            const res = await fetch(url, { headers });
            if (!res.ok) {
                const body = await res.json().catch(() => ({}));
                throw new Error(body.error || `HTTP ${res.status}`);
            }
            setNodes(await res.json());
        } catch (e: any) {
            setError(e.message || 'Failed to load directory');
            setNodes([]);
        } finally {
            setLoading(false);
        }
    }, [mount, glob, accessToken]);

    useEffect(() => { fetchDir(currentPath); }, [currentPath, fetchDir]);
    useEffect(() => { setCurrentPath(initialPath); }, [initialPath]);

    // Build a URL with optional auth token for <img>/<video> src (can't set headers on HTML elements)
    const tokenParam = accessToken ? `token=${encodeURIComponent(accessToken)}` : '';

    // ── Sorted items (with optional ".." at index 0) ────────────

    const canGoUp = currentPath !== '/' && currentPath !== '';
    const sorted = useMemo(() => sortNodes(nodes, sortBy, sortAsc), [nodes, sortBy, sortAsc]);

    // Virtual list: ".." occupies index 0 when canGoUp, real items follow
    const itemCount = sorted.length + (canGoUp ? 1 : 0);
    const getNode = (idx: number): INode | null => {
        if (canGoUp && idx === 0) return null; // ".."
        return sorted[canGoUp ? idx - 1 : idx] ?? null;
    };

    // ── Navigation ──────────────────────────────────────────────

    const openNode = (node: INode) => {
        if (getMimeCategory(node) === 'dir') {
            setCurrentPath(node.path || node.name);
        } else {
            setSelected(prev => prev?.path === node.path ? null : node);
        }
    };

    const goUp = useCallback(() => {
        if (!canGoUp) return;
        const parts = currentPath.replace(/^\/+/, '').split('/').filter(Boolean);
        parts.pop();
        setCurrentPath(parts.length ? parts.join('/') : '/');
    }, [currentPath, canGoUp]);

    const getFileUrl = (node: INode) => {
        const base = `/api/vfs/get/${encodeURIComponent(mount)}/${node.path}`;
        return tokenParam ? `${base}?${tokenParam}` : base;
    };

    const handleView = () => { if (selected) window.open(getFileUrl(selected), '_blank'); };
    const handleDownload = () => {
        if (!selected) return;
        const a = document.createElement('a');
        a.href = getFileUrl(selected);
        a.download = selected.name;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    };

    // ── Sort toggle ─────────────────────────────────────────────

    const cycleSort = () => {
        const keys: SortKey[] = ['name', 'ext', 'date', 'type'];
        const i = keys.indexOf(sortBy);
        if (sortAsc) { setSortAsc(false); } else { setSortBy(keys[(i + 1) % keys.length]); setSortAsc(true); }
    };
    const sortIcons: Record<SortKey, React.ReactNode> = { name: <Type size={16} />, ext: <FileType size={16} />, date: <Clock size={16} />, type: <ArrowUpDown size={16} /> };

    // ── Breadcrumbs ─────────────────────────────────────────────

    const breadcrumbs = useMemo(() => {
        const parts = currentPath.replace(/^\/+/, '').split('/').filter(Boolean);
        const crumbs = [{ label: mount, path: '/' }];
        let acc = '';
        for (const p of parts) { acc += (acc ? '/' : '') + p; crumbs.push({ label: p, path: acc }); }
        return crumbs;
    }, [currentPath, mount]);

    // ── Keyboard navigation ─────────────────────────────────────

    const scrollItemIntoView = useCallback((idx: number) => {
        if (!listRef.current) return;
        const items = listRef.current.querySelectorAll('[data-fb-idx]');
        const el = items[idx] as HTMLElement | undefined;
        el?.scrollIntoView({ block: 'nearest' });
    }, []);

    const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
        if (itemCount === 0) return;

        switch (e.key) {
            case 'ArrowDown':
            case 'ArrowRight':
            case 'j': {
                e.preventDefault();
                const next = focusIdx < itemCount - 1 ? focusIdx + 1 : 0;
                setFocusIdx(next);
                const node = getNode(next);
                if (node) setSelected(node);
                else setSelected(null);
                scrollItemIntoView(next);
                break;
            }
            case 'ArrowUp':
            case 'ArrowLeft':
            case 'k': {
                e.preventDefault();
                const prev = focusIdx > 0 ? focusIdx - 1 : itemCount - 1;
                setFocusIdx(prev);
                const node = getNode(prev);
                if (node) setSelected(node);
                else setSelected(null);
                scrollItemIntoView(prev);
                break;
            }
            case 'Enter':
            case 'l': {
                e.preventDefault();
                if (focusIdx < 0) break;
                const node = getNode(focusIdx);
                if (!node) { goUp(); break; }
                openNode(node);
                break;
            }
            case 'Backspace':
            case 'h': {
                e.preventDefault();
                goUp();
                break;
            }
            case 'Home': {
                e.preventDefault();
                setFocusIdx(0);
                const node = getNode(0);
                if (node) setSelected(node); else setSelected(null);
                scrollItemIntoView(0);
                break;
            }
            case 'End': {
                e.preventDefault();
                const last = itemCount - 1;
                setFocusIdx(last);
                const node = getNode(last);
                if (node) setSelected(node); else setSelected(null);
                scrollItemIntoView(last);
                break;
            }
            case ' ': {
                e.preventDefault();
                if (focusIdx < 0) break;
                const node = getNode(focusIdx);
                const cat = node ? getMimeCategory(node) : null;
                if (node && (cat === 'image' || cat === 'video')) setLightboxNode(node);
                break;
            }
            case 'Escape': {
                e.preventDefault();
                setSelected(null);
                setFocusIdx(-1);
                break;
            }
        }
    }, [focusIdx, itemCount, goUp, scrollItemIntoView, sorted, canGoUp]);

    // Focus container on mount and after nav for keyboard capture
    useEffect(() => { containerRef.current?.focus(); }, [currentPath]);

    const selectedFile = selected && getMimeCategory(selected) !== 'dir' ? selected : null;

    // ── Lightbox state ──────────────────────────────────────────

    const [lightboxNode, setLightboxNode] = useState<INode | null>(null);
    const mediaNodes = useMemo(() => sorted.filter(n => { const c = getMimeCategory(n); return c === 'image' || c === 'video'; }), [sorted]);
    const lightboxIdx = lightboxNode ? mediaNodes.findIndex(n => n.path === lightboxNode.path) : -1;
    const lightboxIsVideo = lightboxNode ? getMimeCategory(lightboxNode) === 'video' : false;

    const lightboxPrev = () => {
        if (lightboxIdx > 0) setLightboxNode(mediaNodes[lightboxIdx - 1]);
    };
    const lightboxNext = () => {
        if (lightboxIdx < mediaNodes.length - 1) setLightboxNode(mediaNodes[lightboxIdx + 1]);
    };
    const closeLightbox = () => {
        setLightboxNode(null);
        setTimeout(() => containerRef.current?.focus(), 0);
    };

    // ── Row click handler ───────────────────────────────────────

    const onItemClick = (idx: number) => {
        setFocusIdx(idx);
        const node = getNode(idx);
        if (!node) return;
        setSelected(prev => prev?.path === node.path ? null : node);
    };
    const onItemDoubleClick = (idx: number) => {
        const node = getNode(idx);
        if (!node) { goUp(); return; }
        const cat = getMimeCategory(node);
        if (cat === 'dir') {
            setCurrentPath(node.path || node.name);
        } else if (cat === 'image' || cat === 'video') {
            setLightboxNode(node);
        } else {
            window.open(getFileUrl(node), '_blank');
        }
    };

    // ── Styles ──────────────────────────────────────────────────

    const focusBg = 'var(--accent, #334155)';
    const selectedBg = 'rgba(59, 130, 246, 0.15)';
    const selectedBorder = 'var(--ring, #3b82f6)';

    // ── Render ──────────────────────────────────────────────────

    return (
        <div
            ref={containerRef}
            tabIndex={0}
            onKeyDown={handleKeyDown}
            style={{
                display: 'flex', flexDirection: 'column', height: '100%',
                border: '1px solid var(--border, #334155)', borderRadius: 6, overflow: 'hidden',
                background: 'var(--background, #0f172a)', color: 'var(--foreground, #e2e8f0)',
                fontFamily: 'var(--font-sans, system-ui, sans-serif)', outline: 'none',
            }}
        >
            <style>{`
        .fb-row:hover { background: var(--accent, #334155) !important; }
        .fb-thumb:hover { border-color: var(--ring, #3b82f6) !important; background: var(--accent, #1e293b) !important; }
        .fb-tb-btn:hover { background: var(--accent, #334155) !important; color: var(--foreground, #e2e8f0) !important; }
        @media (max-width: 767px) { .fb-detail-pane { display: none !important; } }
      `}</style>

            {/* ═══ Toolbar ═══════════════════════════════════ */}
            {showToolbar && (
                <div style={{
                    display: 'flex', alignItems: 'center', gap: 2, padding: '4px 6px',
                    borderBottom: '1px solid var(--border, #334155)',
                    background: 'var(--muted, #1e293b)',
                }}>
                    {/* Navigation */}
                    <button onClick={goUp} disabled={!canGoUp} title="Go up (Backspace)" className="fb-tb-btn"
                        style={{ ...TB_BTN, opacity: canGoUp ? 1 : 0.3, cursor: canGoUp ? 'pointer' : 'default' }}>
                        <ArrowUp size={18} />
                    </button>

                    {/* Breadcrumbs */}
                    <div style={{ flex: 1, display: 'flex', alignItems: 'center', gap: 1, overflow: 'hidden', padding: '0 4px' }}>
                        {breadcrumbs.map((c, i) => (
                            <React.Fragment key={c.path}>
                                {i > 0 && <ChevronRight size={10} style={{ opacity: 0.3, flexShrink: 0 }} />}
                                <button onClick={() => setCurrentPath(c.path)} style={{
                                    background: 'none', border: 'none', cursor: 'pointer',
                                    color: i === breadcrumbs.length - 1 ? 'var(--foreground, #e2e8f0)' : 'var(--muted-foreground, #94a3b8)',
                                    fontWeight: i === breadcrumbs.length - 1 ? 600 : 400,
                                    padding: '2px 3px', borderRadius: 3, whiteSpace: 'nowrap', fontSize: 11,
                                }}>
                                    {c.label}
                                </button>
                            </React.Fragment>
                        ))}
                    </div>

                    <div style={TB_SEP} />

                    {/* File actions — shown when a file is selected */}
                    {selectedFile && (<>
                        <button onClick={handleView} title="View in browser" className="fb-tb-btn" style={TB_BTN}>
                            <ExternalLink size={18} />
                        </button>
                        <button onClick={handleDownload} title="Download" className="fb-tb-btn" style={TB_BTN}>
                            <Download size={18} />
                        </button>
                        <div style={TB_SEP} />
                    </>)}

                    {/* Sort */}
                    <button onClick={cycleSort} title={`Sort: ${sortBy} (${sortAsc ? 'asc' : 'desc'})`} className="fb-tb-btn"
                        style={{ ...TB_BTN, gap: 2 }}>
                        {sortIcons[sortBy]}
                        <span style={{ fontSize: 9, opacity: 0.6 }}>{sortAsc ? '↑' : '↓'}</span>
                    </button>

                    {/* Zoom */}
                    <button onClick={zoomOut} title="Zoom out" className="fb-tb-btn" style={TB_BTN}>
                        <ZoomOut size={18} />
                    </button>
                    <button onClick={zoomIn} title="Zoom in" className="fb-tb-btn" style={TB_BTN}>
                        <ZoomIn size={18} />
                    </button>

                    {/* View mode */}
                    <button onClick={() => setViewMode(v => v === 'list' ? 'thumbs' : 'list')}
                        title={viewMode === 'list' ? 'Thumbnails' : 'List'} className="fb-tb-btn" style={TB_BTN}>
                        {viewMode === 'list' ? <LayoutGrid size={18} /> : <List size={18} />}
                    </button>
                </div>
            )}

            {/* ═══ Content ═══════════════════════════════════ */}
            {loading ? (
                <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, opacity: 0.6 }}>
                    <Loader2 size={16} className="animate-spin" />
                    <span style={{ fontSize: 14 }}>Loading…</span>
                </div>
            ) : error ? (
                <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20, fontSize: 14, color: '#ef4444' }}>
                    {error}
                </div>
            ) : itemCount === 0 ? (
                <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20, fontSize: 14, opacity: 0.5 }}>
                    Empty directory
                </div>
            ) : (
                <div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
                    {/* ── List view ──────────────────────── */}
                    {viewMode === 'list' ? (
                        <div ref={listRef} style={{ overflowY: 'auto', flex: 1 }}>
                            {canGoUp && (
                                <div data-fb-idx={0} onClick={() => { setFocusIdx(0); goUp(); }}
                                    className="fb-row" style={{
                                        display: 'flex', alignItems: 'center', gap: 8, padding: '5px 10px',
                                        cursor: 'pointer', fontSize, borderBottom: '1px solid var(--border, #1e293b)',
                                        background: focusIdx === 0 ? focusBg : 'transparent',
                                    }}>
                                    <ArrowUp size={14} style={{ color: CATEGORY_STYLE.dir.color }} />
                                    <span style={{ fontWeight: 500 }}>..</span>
                                </div>
                            )}
                            {sorted.map((node, i) => {
                                const idx = (canGoUp ? i + 1 : i);
                                const isDir = getMimeCategory(node) === 'dir';
                                const isFocused = focusIdx === idx;
                                const isSelected = selected?.path === node.path;
                                return (
                                    <div key={node.path || node.name} data-fb-idx={idx}
                                        onClick={() => onItemClick(idx)}
                                        onDoubleClick={() => onItemDoubleClick(idx)}
                                        className="fb-row" style={{
                                            display: 'flex', alignItems: 'center', gap: 8, padding: '5px 10px',
                                            cursor: 'pointer', fontSize,
                                            borderBottomWidth: 1, borderBottomColor: 'var(--border, #1e293b)', borderBottomStyle: 'solid',
                                            background: isSelected ? selectedBg : isFocused ? focusBg : 'transparent',
                                            borderLeftWidth: 2, borderLeftColor: isSelected ? selectedBorder : 'transparent',
                                            borderLeftStyle: isSelected ? 'outset' : 'solid',
                                        }}>
                                        <NodeIcon node={node} />
                                        <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{node.name}</span>
                                        {!isDir && (
                                            <span style={{ color: 'var(--muted-foreground, #64748b)', fontSize: 10, flexShrink: 0 }}>
                                                {formatSize(node.size)}
                                            </span>
                                        )}
                                        {mode === 'advanced' && node.mtime && (
                                            <span style={{ color: 'var(--muted-foreground, #64748b)', fontSize: 10, flexShrink: 0, width: 120, textAlign: 'right' }}>
                                                {formatDate(node.mtime)}
                                            </span>
                                        )}
                                    </div>
                                );
                            })}
                        </div>
                    ) : (
                        /* ── Thumb view ─────────────────────── */
                        <div ref={listRef} style={{
                            overflowY: 'auto', flex: 1, display: 'grid',
                            gridTemplateColumns: `repeat(auto-fill, minmax(${thumbSize}px, 1fr))`, gap: 6, padding: 8,
                        }}>
                            {canGoUp && (
                                <div data-fb-idx={0} onClick={() => { setFocusIdx(0); goUp(); }} className="fb-thumb" style={{
                                    display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                                    padding: 8, borderRadius: 6, cursor: 'pointer', gap: 4,
                                    borderWidth: 1, borderColor: focusIdx === 0 ? selectedBorder : 'var(--border, #334155)',
                                    borderStyle: focusIdx === 0 ? 'outset' : 'solid',
                                }}>
                                    <ArrowUp size={24} style={{ color: CATEGORY_STYLE.dir.color }} />
                                    <span style={{ fontSize: 14 }}>..</span>
                                </div>
                            )}
                            {sorted.map((node, i) => {
                                const idx = (canGoUp ? i + 1 : i);
                                const isDir = getMimeCategory(node) === 'dir';
                                const isFocused = focusIdx === idx;
                                const isSelected = selected?.path === node.path;
                                return (
                                    <div key={node.path || node.name} data-fb-idx={idx}
                                        onClick={() => onItemClick(idx)}
                                        onDoubleClick={() => onItemDoubleClick(idx)}
                                        className="fb-thumb" style={{
                                            display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                                            padding: 6, borderRadius: 6, cursor: 'pointer', gap: 4, overflow: 'hidden',
                                            borderWidth: isSelected ? 2 : 1,
                                            borderColor: isSelected ? selectedBorder : isFocused ? selectedBorder : 'transparent',
                                            borderStyle: isSelected ? 'outset' : 'solid',
                                            background: isSelected ? selectedBg : isFocused ? focusBg : 'transparent',
                                        }}>
                                        {isDir ? <NodeIcon node={node} size={28} /> : <ThumbPreview node={node} mount={mount} tokenParam={tokenParam} />}
                                        <span style={{
                                            fontSize: 14, textAlign: 'center', width: '100%',
                                            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                                        }}>
                                            {node.name}
                                        </span>
                                    </div>
                                );
                            })}
                        </div>
                    )}

                    {/* ── Detail panel (advanced, desktop only) ── */}
                    {mode === 'advanced' && selectedFile && (
                        <div className="fb-detail-pane" style={{
                            width: 200, borderLeft: '1px solid var(--border, #334155)',
                            padding: 10, fontSize: 11, overflowY: 'auto', flexShrink: 0,
                            background: 'var(--muted, #1e293b)',
                        }}>
                            <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 10 }}>
                                <Info size={14} />
                                <span style={{ fontWeight: 600 }}>Details</span>
                            </div>

                            {getMimeCategory(selectedFile) === 'image' && (
                                <img src={getFileUrl(selectedFile)} alt={selectedFile.name}
                                    style={{ width: '100%', borderRadius: 4, marginBottom: 8, objectFit: 'contain' }} />
                            )}
                            {getMimeCategory(selectedFile) === 'video' && (
                                <video key={selectedFile.path} src={getFileUrl(selectedFile)} controls muted preload="metadata"
                                    style={{ width: '100%', borderRadius: 4, marginBottom: 8 }} />
                            )}

                            <table style={{ width: '100%', borderCollapse: 'collapse' }}>
                                <tbody>
                                    {([
                                        ['Name', selectedFile.name],
                                        ['Path', selectedFile.path],
                                        ['Size', formatSize(selectedFile.size)],
                                        ['Modified', formatDate(selectedFile.mtime)],
                                        ['MIME', selectedFile.mime || '—'],
                                        ['Type', getMimeCategory(selectedFile)],
                                    ] as [string, string][]).map(([label, val]) => (
                                        <tr key={label}>
                                            <td style={{ padding: '3px 4px 3px 0', color: 'var(--muted-foreground, #94a3b8)', whiteSpace: 'nowrap', verticalAlign: 'top' }}>{label}</td>
                                            <td style={{ padding: '3px 0', wordBreak: 'break-all' }}>{val}</td>
                                        </tr>
                                    ))}
                                </tbody>
                            </table>
                        </div>
                    )}
                </div>
            )}

            {/* ═══ Status bar ════════════════════════════════ */}
            <div style={{
                padding: '3px 10px', fontSize: 10, borderTop: '1px solid var(--border, #334155)',
                color: 'var(--muted-foreground, #64748b)', display: 'flex', justifyContent: 'space-between',
                background: 'var(--muted, #1e293b)',
            }}>
                <span>{sorted.length} item{sorted.length !== 1 ? 's' : ''}{selectedFile ? ` · ${selectedFile.name}` : ''}</span>
                <span>{mount}:{currentPath || '/'}</span>
            </div>

            {/* ═══ Lightbox ═════════════════════════════════ */}
            {lightboxNode && (
                <div onClick={() => closeLightbox()} onKeyDown={(e) => {
                    if (e.key === 'Escape') closeLightbox();
                    if (e.key === 'ArrowLeft') lightboxPrev();
                    if (e.key === 'ArrowRight') lightboxNext();
                    e.stopPropagation();
                }} tabIndex={0} ref={el => el?.focus()} style={{
                    position: 'fixed', inset: 0, zIndex: 9999,
                    background: 'rgba(0,0,0,0.85)', display: 'flex', alignItems: 'center', justifyContent: 'center',
                    cursor: 'zoom-out',
                }}>
                    {/* Close */}
                    <button onClick={() => closeLightbox()} style={{
                        position: 'absolute', top: 16, right: 16, background: 'none', border: 'none',
                        color: '#fff', cursor: 'pointer', opacity: 0.7,
                    }}><X size={24} /></button>

                    {/* Prev */}
                    {lightboxIdx > 0 && (
                        <button onClick={(e) => { e.stopPropagation(); lightboxPrev(); }} style={{
                            position: 'absolute', left: 16, top: '50%', transform: 'translateY(-50%)',
                            background: 'rgba(0,0,0,0.5)', border: 'none', borderRadius: '50%',
                            width: 40, height: 40, display: 'flex', alignItems: 'center', justifyContent: 'center',
                            color: '#fff', cursor: 'pointer',
                        }}><ChevronLeft size={22} /></button>
                    )}

                    {/* Media */}
                    {lightboxIsVideo ? (
                        <video
                            key={lightboxNode.path}
                            onClick={(e) => e.stopPropagation()}
                            src={getFileUrl(lightboxNode)}
                            controls autoPlay
                            style={{ maxWidth: '90vw', maxHeight: '90vh', cursor: 'default', borderRadius: 4, background: '#000' }}
                        />
                    ) : (
                        <img
                            onClick={(e) => e.stopPropagation()}
                            src={getFileUrl(lightboxNode)}
                            alt={lightboxNode.name}
                            style={{ maxWidth: '90vw', maxHeight: '90vh', objectFit: 'contain', cursor: 'default', borderRadius: 4 }}
                        />
                    )}

                    {/* Next */}
                    {lightboxIdx < mediaNodes.length - 1 && (
                        <button onClick={(e) => { e.stopPropagation(); lightboxNext(); }} style={{
                            position: 'absolute', right: 16, top: '50%', transform: 'translateY(-50%)',
                            background: 'rgba(0,0,0,0.5)', border: 'none', borderRadius: '50%',
                            width: 40, height: 40, display: 'flex', alignItems: 'center', justifyContent: 'center',
                            color: '#fff', cursor: 'pointer',
                        }}><ChevronRight size={22} /></button>
                    )}

                    {/* Counter + filename */}
                    <div style={{
                        position: 'absolute', bottom: 16, left: '50%', transform: 'translateX(-50%)',
                        color: '#fff', fontSize: 14, opacity: 0.7, textAlign: 'center',
                    }}>
                        {lightboxNode.name} · {lightboxIdx + 1}/{mediaNodes.length}
                    </div>
                </div>
            )}
        </div>
    );
};

export default FileBrowserWidget;
