import { useEffect, useMemo, useRef, useState } from 'react';
import { Search, Loader2, HardDrive, Download, ChevronRight } from 'lucide-react';
import { toast } from 'sonner';
import { basename, joinPath, putFileContents } from '../webdav';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog';
import { MarqueeText } from './ui/marquee-text';

// ─── API ──────────────────────────────────────────────────────────────────────

const CSDB_API  = 'https://api.idolpx.com/csdb';
const CSDB_HOST = 'https://csdb.dk';
const DOWNLOAD_DIR = '/sd/downloads';

function resolveLink(link: string): string {
  if (link.startsWith('http://') || link.startsWith('https://')) return link;
  return CSDB_HOST + link;
}

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

interface CsdbRow {
  id: string;
  name: string;
  tags: string;
}

interface CsdbDownloadLink {
  Link: string;
  CounterLink: string;
  Downloads: number;
  Status: string;
  Filename: string;
  hash: string;
}

interface CsdbRelease {
  ID: number;
  Name: string;
  Type: string;
  ScreenShot: string[];
  DownloadLinks: CsdbDownloadLink[];
}

// ─── Props ────────────────────────────────────────────────────────────────────

interface SearchCSDbNGProps {
  config: any;
  setConfig: (c: any) => void;
  onClose: () => void;
}

// ─── Module-level persistence ─────────────────────────────────────────────────

const _store = {
  query:       '',
  results:     [] as CsdbRow[],
  hasSearched: false,
  tagFilter:   null as string | null,
  scrollTop:   0,
};

// ─── Helpers ──────────────────────────────────────────────────────────────────

// "c64 crack" → "crack", "c64 music collection" → "music collection"
function typeLabel(tags: string): string {
  return tags.split(' ').slice(1).join(' ') || tags;
}

// ─── Component ────────────────────────────────────────────────────────────────

export default function SearchCSDbNG({ config, setConfig, onClose: _onClose }: SearchCSDbNGProps) {
  const scrollRef = useRef<HTMLDivElement>(null);

  const [query, setQuery]             = useState(() => _store.query);
  const [results, setResults]         = useState<CsdbRow[]>(() => _store.results);
  const [hasSearched, setHasSearched] = useState(() => _store.hasSearched);
  const [isSearching, setIsSearching] = useState(false);
  const [searchError, setSearchError] = useState<string | null>(null);
  const [tagFilter, setTagFilter]     = useState<string | null>(() => _store.tagFilter);

  const [selectedRow, setSelectedRow]       = useState<CsdbRow | null>(null);
  const [release, setRelease]               = useState<CsdbRelease | null>(null);
  const [loadingRelease, setLoadingRelease] = useState(false);

  const [mountEntry, setMountEntry]   = useState<{ row: CsdbRow; link: CsdbDownloadLink } | null>(null);
  const [downloading, setDownloading] = useState<string | null>(null);

  useEffect(() => {
    _store.query       = query;
    _store.results     = results;
    _store.hasSearched = hasSearched;
    _store.tagFilter   = tagFilter;
  }, [query, results, hasSearched, tagFilter]);

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

  // ── Search ──────────────────────────────────────────────────────────────────

  const doSearch = async (q: string) => {
    if (!q.trim()) return;
    setIsSearching(true);
    setSearchError(null);
    setTagFilter(null);
    try {
      const res = await fetch(`${CSDB_API}/search/${encodeURIComponent(q.trim())}`);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data: { count: number; rows: CsdbRow[] } = await res.json();
      setResults(data.rows ?? []);
      setHasSearched(true);
    } catch (e: any) {
      setSearchError(e?.message ?? 'Search failed');
    } finally {
      setIsSearching(false);
    }
  };

  // ── Release detail ───────────────────────────────────────────────────────────

  const openRelease = async (row: CsdbRow) => {
    setSelectedRow(row);
    setRelease(null);
    setLoadingRelease(true);
    try {
      const res = await fetch(`${CSDB_API}/release/${row.id}`);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data: CsdbRelease = await res.json();
      setRelease(data);
    } catch (e: any) {
      toast.error(`Failed to load release: ${e?.message ?? e}`);
      setRelease({ ID: +row.id, Name: row.name, Type: '', ScreenShot: [], DownloadLinks: [] });
    } finally {
      setLoadingRelease(false);
    }
  };

  // ── Download to SD ───────────────────────────────────────────────────────────

  const downloadToSd = async (row: CsdbRow, link: CsdbDownloadLink) => {
    const url = resolveLink(link.Link);
    setDownloading(url);
    try {
      const res = await fetch(url);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data = await res.arrayBuffer();
      const fname = link.Filename || basename(link.Link) || row.name;
      const dest = joinPath(DOWNLOAD_DIR, fname);
      await putFileContents(dest, data);
      toast.success(`Saved to ${dest}`);
    } catch (e: any) {
      toast.error(`Download failed: ${e?.message ?? e}`);
    } finally {
      setDownloading(null);
    }
  };

  // ── Mount ────────────────────────────────────────────────────────────────────

  const mountOnDevice = (deviceType: 'drive' | 'meatloaf', key: string) => {
    if (!mountEntry) return;
    const { row, link } = mountEntry;
    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 = resolveLink(link.Link);
    delete dev.media_set;
    if (!dev.enabled) dev.enabled = 1;
    setConfig(newConfig);
    setMountEntry(null);
    toast.success(`Mounted "${link.Filename || row.name}" on ${deviceType} #${key}`);
  };

  const mountedUrls = useMemo(() => {
    const s = new Set<string>();
    for (const d of Object.values(config?.devices?.iec ?? {})) {
      const dev = d as any;
      if (dev?.url) s.add(dev.url);
    }
    return s;
  }, [config]);

  // ── Derived ──────────────────────────────────────────────────────────────────

  const tagTypes = useMemo(() => {
    const set = new Set<string>();
    for (const r of results) set.add(typeLabel(r.tags));
    return [...set].sort();
  }, [results]);

  const visibleResults = useMemo(() =>
    tagFilter ? results.filter(r => typeLabel(r.tags) === tagFilter) : results,
    [results, tagFilter]
  );

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

  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-200/70">
          <div className="flex gap-2 mb-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' && !isSearching && doSearch(query)}
                placeholder="Search CSDb…"
                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"
                disabled={isSearching}
              />
            </div>
            <button
              onClick={() => doSearch(query)}
              disabled={isSearching || !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"
            >
              {isSearching ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Search'}
            </button>
          </div>

          {/* Type filter chips */}
          {tagTypes.length > 1 && (
            <div className="flex gap-1.5 overflow-x-auto pb-0.5" style={{ scrollbarWidth: 'none' }}>
              <button
                onClick={() => setTagFilter(null)}
                className={`flex-shrink-0 px-2.5 py-0.5 rounded-full text-xs font-medium transition-colors ${tagFilter === null ? 'bg-blue-600 text-white' : 'bg-neutral-100 text-neutral-600 hover:bg-neutral-200'}`}
              >
                All
              </button>
              {tagTypes.map(t => (
                <button
                  key={t}
                  onClick={() => setTagFilter(tagFilter === t ? null : t)}
                  className={`flex-shrink-0 px-2.5 py-0.5 rounded-full text-xs font-medium transition-colors ${tagFilter === t ? 'bg-blue-600 text-white' : 'bg-neutral-100 text-neutral-600 hover:bg-neutral-200'}`}
                >
                  {t}
                </button>
              ))}
            </div>
          )}
        </div>

        {/* Body */}
        <div
          ref={scrollRef}
          className="flex-1 overflow-y-auto"
          onScroll={e => { _store.scrollTop = (e.currentTarget as HTMLDivElement).scrollTop; }}
        >
          {isSearching && (
            <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">Searching…</p>
            </div>
          )}

          {!isSearching && searchError && (
            <div className="p-6 text-center">
              <p className="text-sm text-red-500">{searchError}</p>
              <button onClick={() => doSearch(query)} className="mt-2 text-xs text-blue-600 hover:underline">
                Retry
              </button>
            </div>
          )}

          {!isSearching && !searchError && hasSearched && (
            visibleResults.length > 0 ? (
              <>
                <p className="text-xs text-neutral-400 px-4 py-2 border-b border-neutral-100">
                  {visibleResults.length}{tagFilter ? ` of ${results.length}` : ''} result{visibleResults.length !== 1 ? 's' : ''}
                </p>
                {visibleResults.map(row => (
                  <button
                    key={row.id}
                    onClick={() => openRelease(row)}
                    className="w-full pl-4 pr-4 py-3 flex items-center gap-3 border-b border-neutral-100 border-l-2 border-l-transparent transition-colors hover:bg-blue-50 hover:border-l-blue-400 text-left"
                  >
                    <div className="flex-1 min-w-0">
                      <div className="flex items-center gap-2 flex-wrap">
                        <span className="text-sm font-medium text-neutral-900 truncate">{row.name}</span>
                        <span className="text-xs px-1.5 py-0.5 rounded bg-blue-100 text-blue-700 font-mono flex-shrink-0">{typeLabel(row.tags)}</span>
                      </div>
                    </div>
                    <ChevronRight className="w-4 h-4 text-neutral-300 flex-shrink-0" />
                  </button>
                ))}
              </>
            ) : (
              <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">No results</p>
              </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 the CSDb database</p>
              <p className="text-xs text-neutral-400">Commodore Scene Database — games, demos, music and more</p>
            </div>
          )}
        </div>
      </div>

      {/* Release detail dialog */}
      <Dialog open={selectedRow !== null} onOpenChange={open => !open && setSelectedRow(null)}>
        <DialogContent className="max-w-sm flex flex-col max-h-[90dvh]">
          <DialogHeader className="flex-shrink-0 overflow-hidden min-w-0">
            <DialogTitle className="sr-only">{selectedRow?.name}</DialogTitle>
            <p className="text-lg font-semibold leading-none pr-6 overflow-hidden min-w-0">
              <MarqueeText>{selectedRow?.name}</MarqueeText>
            </p>
            <DialogDescription>
              {[release?.Type, selectedRow?.tags.split(' ')[0]?.toUpperCase()].filter(Boolean).join(' · ')}
            </DialogDescription>
          </DialogHeader>

          <div className="overflow-y-auto flex-1 min-h-0">
            {loadingRelease && (
              <div className="flex justify-center py-8">
                <Loader2 className="w-6 h-6 animate-spin text-blue-500" />
              </div>
            )}
            {!loadingRelease && release?.DownloadLinks.length === 0 && (
              <p className="text-sm text-neutral-400 text-center py-8">No download links found</p>
            )}
            {!loadingRelease && release && release.DownloadLinks.length > 0 && (
              <div className="flex flex-col gap-2 py-1">
                {release.DownloadLinks.map((link, i) => {
                  const url       = resolveLink(link.Link);
                  const isMounted = mountedUrls.has(url);
                  const fname     = link.Filename || basename(link.Link) || selectedRow!.name;
                  return (
                    <div
                      key={i}
                      className={`px-4 py-3 rounded-lg border flex items-center gap-3 ${isMounted ? 'border-blue-300 bg-blue-50' : 'border-neutral-200'}`}
                    >
                      <div className="flex-1 min-w-0">
                        <div className="text-sm font-medium text-neutral-800 truncate">{fname}</div>
                        <div className="text-xs text-neutral-400">
                          {link.Downloads > 0 ? `${link.Downloads.toLocaleString()} downloads` : link.Status}
                        </div>
                      </div>
                      <button
                        onClick={() => downloadToSd(selectedRow!, link)}
                        disabled={downloading === url}
                        className="p-1.5 rounded-lg hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600 disabled:opacity-40"
                        title="Download to /sd/downloads"
                      >
                        {downloading === url
                          ? <Loader2 className="w-4 h-4 animate-spin" />
                          : <Download className="w-4 h-4" />}
                      </button>
                      <button
                        onClick={() => setMountEntry({ row: selectedRow!, link })}
                        className={`p-1.5 rounded-lg ${isMounted ? 'text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`}
                        title="Mount on virtual drive"
                      >
                        <HardDrive className="w-4 h-4" />
                      </button>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        </DialogContent>
      </Dialog>

      {/* Mount device picker */}
      <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">
              {mountEntry ? (mountEntry.link.Filename || mountEntry.row.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) && (
                          <div
                            className="text-xs text-neutral-500 overflow-hidden whitespace-nowrap"
                            style={{ direction: 'rtl', textOverflow: 'ellipsis' }}
                            title={[dev.base_url, dev.url].filter(Boolean).join('')}
                          >
                            <span style={{ direction: 'ltr', unicodeBidi: 'embed' }}>
                              {[dev.base_url, dev.url].filter(Boolean).join('')}
                            </span>
                          </div>
                        )}
                      </div>
                      {!dev.enabled && <span className="text-xs text-neutral-400 flex-shrink-0">disabled</span>}
                    </button>
                  ))}
                </div>
              );
            })()}
          </div>
        </DialogContent>
      </Dialog>
    </>
  );
}
