import { useEffect, useMemo, useRef, useState } from 'react';
import { flushSync } from 'react-dom';
import { Search, Loader2, RefreshCw, FolderSearch, SlidersHorizontal, ArrowUp, ArrowDown, ArrowUpDown, HardDrive, FolderOpen } from 'lucide-react';
import { toast } from 'sonner';
import { humanFileSize, splitPath } from '../webdav';
import type { EntryInfo } from '../webdav';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog';
import { MediaEntry } from './MediaEntry';
import { MarqueeText } from './ui/marquee-text';
import {
  openLocateDb,
  searchLocate,
  isLocateDbLoaded,
  resetLocateDb,
  buildLocateDb,
  type LocateEntry,
  type ScanPhase,
} from '../locate-db';

interface SearchLocalProps {
  config: any;
  setConfig: (config: any) => void;
  onClose: () => void;
  onOpenFolder: (path: string) => void;
}

interface SearchResult {
  name: string;
  path: string;
  type: string;
  size: number;
  sizeText: string;
  isDir: boolean;
  system: string | null;
  video: string | null;
  language: string | null;
}

const DISK_EXTS = new Set(['d64','d71','d81','d82','dnp','t64','tap','g64','nib']);
const CART_EXTS = new Set(['crt','bin']);
const PRG_EXTS  = new Set(['prg','p00']);

const LANG_CODES = ['En','De','Fr','Es','It','Nl','Sv','Da','Fi','Pt','Pl','Ru','Ja','Ko','Zh','Cs','Hu','No'];
const LANG_NAMES: Record<string, string> = {
  En:'English', De:'German', Fr:'French', Es:'Spanish', It:'Italian',
  Nl:'Dutch', Sv:'Swedish', Da:'Danish', Fi:'Finnish', Pt:'Portuguese',
  Pl:'Polish', Ru:'Russian', Ja:'Japanese', Ko:'Korean', Zh:'Chinese',
  Cs:'Czech', Hu:'Hungarian', No:'Norwegian',
};
const LANG_RE = new RegExp(
  '\\((' + LANG_CODES.join('|') + ')(?:[,+](?:' + LANG_CODES.join('|') + '))*\\)',
  'i'
);

const VIDEO_RE = /\b(PAL(?:[_-]?(?:60|NTSC))?|NTSC(?:[_-]?PAL)?)\b/i;
const VIDEO_NORM: Record<string, string> = {
  'PAL': 'PAL', 'PAL60': 'PAL60', 'PAL-60': 'PAL60', 'PAL_60': 'PAL60',
  'PAL-NTSC': 'PAL/NTSC', 'PAL_NTSC': 'PAL/NTSC', 'NTSC-PAL': 'PAL/NTSC', 'NTSC_PAL': 'PAL/NTSC',
  'NTSC': 'NTSC',
};

const SYSTEM_RE = /\b(C[-_]?64|C[-_]?128|VIC[-_]?20|Plus\/?4|C[-_]?16|PET|C[-_]?64DTV)\b/i;
const SYSTEM_NORM: Record<string, string> = {
  'C64':'C64','C-64':'C64','C_64':'C64',
  'C128':'C128','C-128':'C128','C_128':'C128',
  'VIC20':'VIC-20','VIC-20':'VIC-20','VIC_20':'VIC-20',
  'PLUS4':'Plus/4','PLUS/4':'Plus/4',
  'C16':'C16','C-16':'C16','C_16':'C16',
  'PET':'PET',
  'C64DTV':'C64DTV','C-64DTV':'C64DTV',
};

function fileExtension(p: string): string {
  const dot = p.lastIndexOf('.');
  return dot < 0 ? '' : p.slice(dot + 1).toLowerCase();
}

function parseTags(path: string): { system: string | null; video: string | null; language: string | null } {
  const videoM = path.match(VIDEO_RE);
  const video = videoM ? (VIDEO_NORM[videoM[1].toUpperCase().replace(/[-_]/g, '-')] ?? videoM[1].toUpperCase()) : null;

  const sysM = path.match(SYSTEM_RE);
  const system = sysM ? (SYSTEM_NORM[sysM[1].toUpperCase().replace(/[-_]/g, '')] ?? sysM[1]) : null;

  const langM = path.match(LANG_RE);
  const language = langM ? (LANG_NAMES[langM[1]] ?? langM[1]) : null;

  return { system, video, language };
}

function entryToResult(e: LocateEntry): SearchResult {
  const tags = parseTags(e.path);
  if (e.is_dir) {
    return { name: e.name, path: e.path, type: 'DIR', size: 0, sizeText: '—', isDir: true, ...tags };
  }
  const ext = fileExtension(e.name);
  return {
    name: e.name, path: e.path,
    type: ext ? ext.toUpperCase() : 'FILE',
    size: e.size, sizeText: humanFileSize(e.size),
    isDir: false,
    ...tags,
  };
}

function resultToEntry(r: SearchResult): EntryInfo {
  return { name: r.name, path: r.path, type: r.isDir ? 'folder' : 'file', size: r.size, lastModified: null, contentType: null };
}

function TypeBadge({ type, isDir }: { type: string; isDir: boolean }) {
  if (isDir) return <span className="text-xs px-1.5 py-0.5 rounded bg-amber-100 text-amber-700 font-mono">DIR</span>;
  const ext = type.toLowerCase();
  if (DISK_EXTS.has(ext)) return <span className="text-xs px-1.5 py-0.5 rounded bg-blue-100 text-blue-700 font-mono">{type}</span>;
  if (CART_EXTS.has(ext)) return <span className="text-xs px-1.5 py-0.5 rounded bg-purple-100 text-purple-700 font-mono">{type}</span>;
  if (PRG_EXTS.has(ext))  return <span className="text-xs px-1.5 py-0.5 rounded bg-green-100 text-green-700 font-mono">{type}</span>;
  return <span className="text-xs px-1.5 py-0.5 rounded bg-neutral-100 text-neutral-600 font-mono">{type}</span>;
}

function scanPhaseLabel(phase: ScanPhase, value: number): string {
  if (phase === 'scanning') return `Scanning… ${humanFileSize(value)}`;
  if (phase === 'building') return `Building… ${value.toLocaleString()} entries`;
  return `Saving… ${humanFileSize(value)}`;
}

type SortField = 'name' | 'size';
type SortDir = 'asc' | 'desc';

const _store = {
  query:          '',
  results:        [] as SearchResult[],
  hasSearched:    false,
  showFilter:     false,
  filterSystem:   null as string | null,
  filterVideo:    null as string | null,
  filterLanguage: null as string | null,
  sortField:      'name' as SortField,
  sortDir:        'asc'  as SortDir,
  scrollTop:      0,
};

function FilterChips({
  label, values, selected, onSelect,
}: {
  label: string;
  values: string[];
  selected: string | null;
  onSelect: (v: string | null) => void;
}) {
  if (values.length === 0) return null;
  return (
    <div className="flex items-center gap-1.5 flex-wrap">
      <span className="text-xs text-neutral-400 font-medium w-14 flex-shrink-0">{label}</span>
      {values.map(v => (
        <button
          key={v}
          onClick={() => onSelect(selected === v ? null : v)}
          className={`px-2.5 py-0.5 rounded-full text-xs font-medium transition-colors ${selected === v ? 'bg-blue-600 text-white' : 'bg-neutral-100 text-neutral-600 hover:bg-neutral-200'}`}
        >
          {v}
        </button>
      ))}
    </div>
  );
}

export default function SearchLocal({ config, setConfig, onClose, onOpenFolder }: SearchLocalProps) {
  const scrollRef = useRef<HTMLDivElement>(null);
  const [query, setQuery] = useState(() => _store.query);
  const [isSearching, setIsSearching] = useState(false);
  const [isScanning, setIsScanning] = useState(false);
  const [scanPhase, setScanPhase] = useState<ScanPhase | null>(null);
  const [scanValue, setScanValue] = useState(0);
  const [results, setResults] = useState<SearchResult[]>(() => _store.results);
  const [hasSearched, setHasSearched] = useState(() => _store.hasSearched);
  const [mountEntry, setMountEntry] = useState<SearchResult | null>(null);
  const [actionEntry, setActionEntry] = useState<SearchResult | null>(null);
  const [searchError, setSearchError] = useState<string | null>(null);
  const [dbBytes, setDbBytes] = useState<number | null>(null);
  const [dbPhase, setDbPhase] = useState<'idle' | 'downloading' | 'ready'>('idle');
  const [showFilter, setShowFilter] = useState(() => _store.showFilter);
  const [filterSystem, setFilterSystem] = useState<string | null>(() => _store.filterSystem);
  const [filterVideo, setFilterVideo] = useState<string | null>(() => _store.filterVideo);
  const [filterLanguage, setFilterLanguage] = useState<string | null>(() => _store.filterLanguage);
  const [sortField, setSortField] = useState<SortField>(() => _store.sortField);
  const [sortDir, setSortDir] = useState<SortDir>(() => _store.sortDir);

  useEffect(() => {
    if (isLocateDbLoaded()) setDbPhase('ready');
  }, []);

  useEffect(() => {
    _store.query          = query;
    _store.results        = results;
    _store.hasSearched    = hasSearched;
    _store.showFilter     = showFilter;
    _store.filterSystem   = filterSystem;
    _store.filterVideo    = filterVideo;
    _store.filterLanguage = filterLanguage;
    _store.sortField      = sortField;
    _store.sortDir        = sortDir;
  }, [query, results, hasSearched, showFilter, filterSystem, filterVideo, filterLanguage, sortField, sortDir]);

  useEffect(() => {
    if (_store.scrollTop > 0 && scrollRef.current) {
      scrollRef.current.scrollTop = _store.scrollTop;
    }
  }, []);

  const mountedPaths = useMemo(() => {
    const paths = new Set<string>();
    for (const d of Object.values(config?.devices?.iec ?? {})) {
      const dev = d as any;
      if (!dev?.url) continue;
      const base = (dev.base_url ?? '').replace(/\/$/, '');
      const url  = dev.url.startsWith('/') ? dev.url : '/' + dev.url;
      paths.add(base ? base + url : dev.url);
    }
    return paths;
  }, [config]);

  const handleSearch = async () => {
    if (!query.trim()) { toast.error('Please enter a search term'); return; }
    setIsSearching(true);
    setHasSearched(true);
    setSearchError(null);
    setFilterSystem(null);
    setFilterVideo(null);
    setFilterLanguage(null);
    try {
      if (!isLocateDbLoaded()) {
        setDbPhase('downloading');
        setDbBytes(null);
        await openLocateDb(bytes => flushSync(() => setDbBytes(bytes)));
        setDbPhase('ready');
      }
      const needle = query.trim();
      const entries = searchLocate(needle);
      const hasWildcard = /[*?]/.test(needle);
      entries.sort((a, b) => {
        if (!hasWildcard) {
          const lower = needle.toLowerCase();
          const aStart = a.name.toLowerCase().startsWith(lower) ? 0 : 1;
          const bStart = b.name.toLowerCase().startsWith(lower) ? 0 : 1;
          if (aStart !== bStart) return aStart - bStart;
        }
        return a.path.length - b.path.length;
      });
      setResults(entries.map(entryToResult));
    } catch (e: any) {
      setSearchError(e?.message ?? 'Search failed');
      resetLocateDb();
      setDbPhase('idle');
    } finally {
      setIsSearching(false);
    }
  };

  const handleScan = async () => {
    setIsScanning(true);
    setScanPhase('scanning');
    setScanValue(0);
    try {
      const { count, bytes } = await buildLocateDb((phase, value) =>
        flushSync(() => { setScanPhase(phase); setScanValue(value); })
      );
      toast.success(`Indexed ${count.toLocaleString()} items · ${humanFileSize(bytes)}`);
      setDbPhase('ready');
    } catch (e: any) {
      toast.error(`Scan failed: ${e?.message ?? e}`);
    } finally {
      setIsScanning(false);
      setScanPhase(null);
    }
  };

  const handleRefreshDb = () => {
    resetLocateDb();
    setDbPhase('idle');
    setHasSearched(false);
    setResults([]);
    toast.info('Database will be reloaded on next search');
  };

  const mountOnDevice = (deviceType: 'drive' | 'meatloaf', key: string) => {
    if (!mountEntry) return;
    const newConfig = JSON.parse(JSON.stringify(config));
    if (!newConfig.devices) newConfig.devices = {};
    if (!newConfig.devices.iec) newConfig.devices.iec = {};
    if (!newConfig.devices.iec[key]) newConfig.devices.iec[key] = { type: deviceType };
    const dev = newConfig.devices.iec[key];
    dev.url = mountEntry.path;
    delete dev.media_set;
    if (!dev.enabled) dev.enabled = 1;
    setConfig(newConfig);
    setMountEntry(null);
    toast.success(`Mounted "${mountEntry.name}" on ${deviceType} #${key}`);
  };

  const loadingLabel = dbPhase === 'downloading'
    ? (dbBytes === null ? 'Loading database…' : `Loading database… ${humanFileSize(dbBytes)}`)
    : 'Searching…';

  const busy = isSearching || isScanning;

  const facets = useMemo(() => {
    const systems = [...new Set(results.map(r => r.system).filter(Boolean) as string[])].sort();
    const videos  = [...new Set(results.map(r => r.video).filter(Boolean) as string[])].sort();
    const langs   = [...new Set(results.map(r => r.language).filter(Boolean) as string[])].sort();
    return { systems, videos, langs };
  }, [results]);

  const hasAnyFacets = facets.systems.length > 0 || facets.videos.length > 0 || facets.langs.length > 0;

  const visibleResults = useMemo(() => {
    let list = results.filter(r =>
      (filterSystem  === null || r.system   === filterSystem) &&
      (filterVideo   === null || r.video    === filterVideo) &&
      (filterLanguage === null || r.language === filterLanguage)
    );
    list = [...list].sort((a, b) => {
      const cmp = sortField === 'name' ? a.name.localeCompare(b.name) : a.size - b.size;
      return sortDir === 'asc' ? cmp : -cmp;
    });
    return list;
  }, [results, filterSystem, filterVideo, filterLanguage, sortField, sortDir]);

  const toggleSort = (field: SortField) => {
    if (sortField === field) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
    else { setSortField(field); setSortDir('asc'); }
  };

  const SortIcon = ({ field }: { field: SortField }) => {
    if (sortField !== field) return <ArrowUpDown className="w-3 h-3 opacity-50" />;
    return sortDir === 'asc' ? <ArrowUp className="w-3 h-3" /> : <ArrowDown className="w-3 h-3" />;
  };

  const activeFilters = [filterSystem, filterVideo, filterLanguage].filter(Boolean).length;

  return (
    <>
      <div className="flex flex-col h-full overflow-hidden">
        {/* Header */}
        <div className="flex-shrink-0 px-4 pt-3 pb-3 border-b border-neutral-100">
          <div className="flex items-center justify-end mb-3 gap-1">
            {dbPhase === 'ready' && !busy && (
              <button
                onClick={handleRefreshDb}
                className="p-1.5 rounded-lg hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600 transition-colors"
                title="Reload database"
              >
                <RefreshCw className="w-4 h-4" />
              </button>
            )}
            <button
              onClick={handleScan}
              disabled={busy}
              className="p-1.5 rounded-lg hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600 disabled:opacity-40 transition-colors"
              title="Scan /sd and rebuild database"
            >
              <FolderSearch className="w-4 h-4" />
            </button>
            {hasAnyFacets && (
              <button
                onClick={() => setShowFilter(v => !v)}
                className={`relative p-1.5 rounded-lg transition-colors ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`}
                title="Filter & sort"
              >
                <SlidersHorizontal className="w-4 h-4" />
                {activeFilters > 0 && (
                  <span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
                    {activeFilters}
                  </span>
                )}
              </button>
            )}
          </div>

          {/* Search input */}
          <div className="flex gap-2">
            <div className="relative flex-1">
              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-400 pointer-events-none" />
              <input
                type="text"
                value={query}
                onChange={e => setQuery(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && !busy && handleSearch()}
                placeholder="Search… (* any chars, ? one char)"
                className="w-full pl-9 pr-3 py-2.5 bg-neutral-100 border-0 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-colors"
                autoFocus
                disabled={busy}
              />
            </div>
            <button
              onClick={handleSearch}
              disabled={busy || !query.trim()}
              className="px-4 py-2.5 bg-blue-600 text-white rounded-xl text-sm font-medium hover:bg-blue-700 disabled:opacity-40 transition-colors"
            >
              Search
            </button>
          </div>

          {/* Filter panel */}
          <div className={`overflow-hidden transition-all duration-200 ease-in-out ${showFilter && hasAnyFacets ? 'max-h-48 opacity-100 mt-3' : 'max-h-0 opacity-0'}`}>
            <div className="space-y-2 pb-1">
              <FilterChips label="System"   values={facets.systems} selected={filterSystem}   onSelect={setFilterSystem} />
              <FilterChips label="Video"    values={facets.videos}  selected={filterVideo}    onSelect={setFilterVideo} />
              <FilterChips label="Language" values={facets.langs}   selected={filterLanguage} onSelect={setFilterLanguage} />
              <div className="flex items-center gap-1.5 pt-0.5 border-t border-neutral-100">
                <span className="text-xs text-neutral-400 font-medium w-14 flex-shrink-0">Sort</span>
                <button
                  onClick={() => toggleSort('name')}
                  className={`flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium transition-colors ${sortField === 'name' ? 'bg-blue-600 text-white' : 'bg-neutral-100 text-neutral-600 hover:bg-neutral-200'}`}
                >
                  Name <SortIcon field="name" />
                </button>
                <button
                  onClick={() => toggleSort('size')}
                  className={`flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium transition-colors ${sortField === 'size' ? 'bg-blue-600 text-white' : 'bg-neutral-100 text-neutral-600 hover:bg-neutral-200'}`}
                >
                  Size <SortIcon field="size" />
                </button>
              </div>
            </div>
          </div>
        </div>

        {/* Body */}
        <div
          ref={scrollRef}
          className="flex-1 overflow-y-auto"
          onScroll={e => { _store.scrollTop = (e.currentTarget as HTMLDivElement).scrollTop; }}
        >
          {isScanning && scanPhase && (
            <div className="flex flex-col items-center justify-center py-16 gap-3">
              <Loader2 className="w-8 h-8 text-blue-500 animate-spin" />
              <p className="text-sm font-medium text-neutral-700">{scanPhaseLabel(scanPhase, scanValue)}</p>
              <p className="text-xs text-neutral-400">Scanning /sd recursively…</p>
            </div>
          )}

          {isSearching && !hasSearched && (
            <div className="flex flex-col items-center justify-center py-16 gap-3">
              <Loader2 className="w-8 h-8 text-blue-500 animate-spin" />
              <p className="text-sm text-neutral-500">{loadingLabel}</p>
            </div>
          )}

          {!busy && searchError && (
            <div className="p-6 text-center">
              <p className="text-sm text-red-600">{searchError}</p>
            </div>
          )}

          {!searchError && hasSearched && (
            <div>
              {visibleResults.length > 0 ? (
                <>
                  <p className="text-xs text-neutral-400 px-4 py-2 flex items-center gap-1.5 border-b border-neutral-100">
                    {isSearching && <Loader2 className="w-3 h-3 animate-spin" />}
                    {visibleResults.length}{results.length !== visibleResults.length ? ` of ${results.length}` : ''} result{visibleResults.length !== 1 ? 's' : ''}
                  </p>
                  <div>
                    {visibleResults.map((result) => (
                      <MediaEntry
                        key={result.path}
                        entry={resultToEntry(result)}
                        onPrimaryClick={() => setMountEntry(result)}
                        onActionsClick={e => { e.stopPropagation(); setActionEntry(result); }}
                        selected={mountedPaths.has(result.path)}
                        nameSlot={
                          <>
                            <div className="flex items-center gap-1.5 flex-wrap">
                              <span className="text-neutral-900 text-sm">{result.name}</span>
                              <TypeBadge type={result.type} isDir={result.isDir} />
                              {result.system   && <span className="text-xs px-1.5 py-0.5 rounded bg-indigo-100 text-indigo-700 font-mono">{result.system}</span>}
                              {result.video    && <span className="text-xs px-1.5 py-0.5 rounded bg-teal-100 text-teal-700 font-mono">{result.video}</span>}
                              {result.language && <span className="text-xs px-1.5 py-0.5 rounded bg-rose-100 text-rose-700 font-mono">{result.language}</span>}
                            </div>
                            <div className="text-xs text-neutral-400 truncate">{result.path}</div>
                          </>
                        }
                      />
                    ))}
                  </div>
                </>
              ) : (
                <div className="py-16 text-center">
                  <Search className="w-10 h-10 mx-auto mb-3 text-neutral-300" />
                  <p className="text-sm text-neutral-500">
                    {results.length > 0 ? 'No results match the current filters' : `No results for "${query}"`}
                  </p>
                  {results.length > 0 && (
                    <button
                      onClick={() => { setFilterSystem(null); setFilterVideo(null); setFilterLanguage(null); }}
                      className="mt-2 text-xs text-blue-600 hover:underline"
                    >
                      Clear filters
                    </button>
                  )}
                </div>
              )}
            </div>
          )}

          {!isSearching && !hasSearched && (
            <div className="py-16 text-center px-6">
              <Search className="w-10 h-10 mx-auto mb-3 text-neutral-300" />
              <p className="text-sm font-medium text-neutral-600 mb-1">Search your device</p>
              <p className="text-xs text-neutral-400">
                {dbPhase === 'idle'
                  ? 'The locate database will be downloaded on first search, or tap the scan icon to rebuild it.'
                  : 'Type a filename or path to search the index.'}
              </p>
            </div>
          )}
        </div>
      </div>

      {/* Actions dialog */}
      <Dialog open={actionEntry !== null} onOpenChange={open => !open && setActionEntry(null)}>
        <DialogContent className="max-w-sm">
          <DialogHeader className="overflow-hidden min-w-0">
            <DialogTitle className="sr-only">{actionEntry?.name}</DialogTitle>
            <p className="text-lg font-semibold leading-none pr-6 overflow-hidden min-w-0">
              <MarqueeText>{actionEntry?.name}</MarqueeText>
            </p>
            <DialogDescription>{actionEntry?.isDir ? 'Folder' : actionEntry?.sizeText}</DialogDescription>
          </DialogHeader>
          <div className="flex flex-col gap-2">
            {!actionEntry?.isDir && (
              <button
                onClick={() => { setMountEntry(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"
              >
                <HardDrive className="w-4 h-4 text-amber-600" /> <span>Mount on virtual drive</span>
              </button>
            )}
            <button
              onClick={() => {
                const folder = actionEntry ? splitPath(actionEntry.path).parent : '/';
                setActionEntry(null);
                onClose();
                onOpenFolder(folder);
              }}
              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"
            >
              <FolderOpen className="w-4 h-4 text-blue-600" /> <span>Open containing folder</span>
            </button>
          </div>
        </DialogContent>
      </Dialog>

      {/* Mount dialog */}
      <Dialog open={mountEntry !== null} onOpenChange={open => !open && setMountEntry(null)}>
        <DialogContent className="max-w-sm flex flex-col max-h-[90dvh]">
          <DialogHeader className="flex-shrink-0">
            <DialogTitle>Mount on Virtual Drive</DialogTitle>
            <DialogDescription className="truncate" title={mountEntry?.name}>
              {mountEntry?.name}
            </DialogDescription>
          </DialogHeader>
          <div className="overflow-y-auto flex-1 min-h-0">
            {(() => {
              const allDevices = Object.entries(config?.devices?.iec ?? {});
              const drives = allDevices
                .filter(([, v]: [string, any]) => (v as any)?.type === 'drive')
                .map(([k, v]: [string, any]) => ({ type: 'drive' as const, key: k, base_url: v?.base_url as string | undefined, url: v?.url as string | undefined, enabled: !!v?.enabled }));
              const meatloafs = allDevices
                .filter(([, v]: [string, any]) => (v as any)?.type === 'meatloaf')
                .map(([k, v]: [string, any]) => ({ type: 'meatloaf' as const, key: k, base_url: v?.base_url as string | undefined, url: v?.url as string | undefined, enabled: !!v?.enabled }));
              const devices = [...drives, ...meatloafs];
              if (!devices.length)
                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.type}-${dev.key}`}
                      onClick={() => mountOnDevice(dev.type, dev.key)}
                      className="w-full text-left px-4 py-3 rounded border border-neutral-200 hover:bg-blue-50 hover:border-blue-300 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.base_url || dev.url) && (() => {
                          const displayUrl = [dev.base_url, dev.url].filter(Boolean).join('');
                          return (
                            <div
                              className="text-xs text-neutral-500 overflow-hidden whitespace-nowrap"
                              style={{ direction: 'rtl', textOverflow: 'ellipsis' }}
                              title={displayUrl}
                            >
                              <span style={{ direction: 'ltr', unicodeBidi: 'embed' }}>{displayUrl}</span>
                            </div>
                          );
                        })()}
                      </div>
                      {!dev.enabled && <span className="text-xs text-neutral-400 flex-shrink-0">disabled</span>}
                    </button>
                  ))}
                </div>
              );
            })()}
          </div>
        </DialogContent>
      </Dialog>
    </>
  );
}
