import { useState, useEffect, useRef, useCallback } from 'react';
import Globe from 'globe.gl';

// ── Types ──
interface RadioPlace {
  id: string;
  geo: [number, number];
  title: string;
  country: string;
  size: number;
}

interface RadioChannel {
  id: string;
  title: string;
}

interface NowPlaying {
  stationId: string;
  stationName: string;
  placeName: string;
  country: string;
  startedAt: string;
  channelName: string;
}

interface GuildInfo {
  id: string;
  name: string;
  voiceChannels: { id: string; name: string; members: number }[];
}

interface SearchHit {
  id: string;
  type: string;
  title: string;
  subtitle: string;
  url: string;
}

interface Favorite {
  stationId: string;
  stationName: string;
  placeName: string;
  country: string;
  placeId: string;
}

interface VoiceStats {
  voicePing: number | null;
  gatewayPing: number;
  status: string;
  channelName: string | null;
  connectedSince: string | null;
}

const THEMES = [
  { id: 'default', color: '#e67e22', label: 'Sunset' },
  { id: 'purple', color: '#9b59b6', label: 'Midnight' },
  { id: 'forest', color: '#2ecc71', label: 'Forest' },
  { id: 'ocean', color: '#3498db', label: 'Ocean' },
  { id: 'cherry', color: '#e74c6f', label: 'Cherry' },
];

// ── Zoom scaling constants ──
const BASE_ALT = 2.0;

// ── Component ──
export default function RadioTab({ data }: { data: any }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const globeRef = useRef<any>(null);
  const rotationResumeRef = useRef<ReturnType<typeof setTimeout>>(undefined);

  const [theme, setTheme] = useState(() => localStorage.getItem('radio-theme') || 'default');
  const [places, setPlaces] = useState<RadioPlace[]>([]);
  const [selectedPlace, setSelectedPlace] = useState<RadioPlace | null>(null);
  const [stations, setStations] = useState<RadioChannel[]>([]);
  const [stationsLoading, setStationsLoading] = useState(false);
  const [nowPlaying, setNowPlaying] = useState<Record<string, NowPlaying>>({});
  const [guilds, setGuilds] = useState<GuildInfo[]>([]);
  const [selectedGuild, setSelectedGuild] = useState('');
  const [selectedChannel, setSelectedChannel] = useState('');
  const [searchQuery, setSearchQuery] = useState('');
  const [searchResults, setSearchResults] = useState<SearchHit[]>([]);
  const [searchOpen, setSearchOpen] = useState(false);
  const [favorites, setFavorites] = useState<Favorite[]>([]);
  const [showFavorites, setShowFavorites] = useState(false);
  const [playingLoading, setPlayingLoading] = useState(false);
  const [volume, setVolume] = useState(0.5);
  const [voiceStats, setVoiceStats] = useState<VoiceStats | null>(null);
  const [showConnModal, setShowConnModal] = useState(false);
  const [containerVisible, setContainerVisible] = useState(false);
  const searchTimeout = useRef<ReturnType<typeof setTimeout>>(undefined);
  const volumeTimeout = useRef<ReturnType<typeof setTimeout>>(undefined);
  const selectedGuildRef = useRef(selectedGuild);

  // ── Fetch initial data ──
  useEffect(() => {
    fetch('/api/radio/places').then(r => r.json()).then(setPlaces).catch(console.error);

    fetch('/api/radio/guilds')
      .then(r => r.json())
      .then((g: GuildInfo[]) => {
        setGuilds(g);
        if (g.length > 0) {
          setSelectedGuild(g[0].id);
          const ch = g[0].voiceChannels.find(c => c.members > 0) ?? g[0].voiceChannels[0];
          if (ch) setSelectedChannel(ch.id);
        }
      })
      .catch(console.error);

    fetch('/api/radio/favorites').then(r => r.json()).then(setFavorites).catch(console.error);
  }, []);

  // Keep selectedGuildRef in sync
  useEffect(() => { selectedGuildRef.current = selectedGuild; }, [selectedGuild]);

  // ── Handle SSE data ──
  useEffect(() => {
    if (data?.guildId && 'playing' in data && data.type !== 'radio_voicestats') {
      // Per-guild SSE event (broadcastState): playing is single NowPlaying | null
      setNowPlaying(prev => {
        if (data.playing) {
          return { ...prev, [data.guildId]: data.playing };
        }
        const next = { ...prev };
        delete next[data.guildId];
        return next;
      });
    } else if (data?.playing && !data?.guildId) {
      // Snapshot: playing is Record<string, NowPlaying>
      setNowPlaying(data.playing);
    }
    if (data?.favorites) setFavorites(data.favorites);
    // Volume from snapshot or radio_volume event
    if (data?.volumes && selectedGuild && data.volumes[selectedGuild] != null) {
      setVolume(data.volumes[selectedGuild]);
    }
    if (data?.volume != null && data?.guildId === selectedGuild) {
      setVolume(data.volume);
    }
    // Voice stats
    if (data?.type === 'radio_voicestats' && data.guildId === selectedGuildRef.current) {
      setVoiceStats({
        voicePing: data.voicePing,
        gatewayPing: data.gatewayPing,
        status: data.status,
        channelName: data.channelName,
        connectedSince: data.connectedSince,
      });
    }
  }, [data, selectedGuild]);

  // ── Theme persist + update globe colors ──
  useEffect(() => {
    localStorage.setItem('radio-theme', theme);
    if (globeRef.current && containerRef.current) {
      const style = getComputedStyle(containerRef.current.parentElement!);
      const accentRgb = style.getPropertyValue('--accent-rgb').trim();
      globeRef.current
        .pointColor(() => `rgba(${accentRgb}, 0.85)`)
        .atmosphereColor(`rgba(${accentRgb}, 0.25)`);
    }
  }, [theme]);

  // ── Helper: pause globe rotation for 5s (only resumes if no panel open) ──
  const selectedPlaceRef = useRef(selectedPlace);
  selectedPlaceRef.current = selectedPlace;
  const showFavoritesRef = useRef(showFavorites);
  showFavoritesRef.current = showFavorites;

  const pauseRotation = useCallback(() => {
    const controls = globeRef.current?.controls() as any;
    if (controls) controls.autoRotate = false;
    if (rotationResumeRef.current) clearTimeout(rotationResumeRef.current);
    rotationResumeRef.current = setTimeout(() => {
      // Only resume if no sidebar is open
      if (selectedPlaceRef.current || showFavoritesRef.current) return;
      const c = globeRef.current?.controls() as any;
      if (c) c.autoRotate = true;
    }, 5000);
  }, []);

  // ── Stop/resume rotation when panels open/close ──
  useEffect(() => {
    const controls = globeRef.current?.controls() as any;
    if (!controls) return;
    if (selectedPlace || showFavorites) {
      controls.autoRotate = false;
      if (rotationResumeRef.current) clearTimeout(rotationResumeRef.current);
    } else {
      controls.autoRotate = true;
    }
  }, [selectedPlace, showFavorites]);

  // ── Point click handler (stable ref) ──
  const handlePointClickRef = useRef<(point: any) => void>(undefined);
  handlePointClickRef.current = (point: any) => {
    setSelectedPlace(point);
    setShowFavorites(false);
    setStationsLoading(true);
    setStations([]);
    pauseRotation();
    if (globeRef.current) {
      // Radio Garden geo format: [lng, lat]
      globeRef.current.pointOfView({ lat: point.geo[1], lng: point.geo[0], altitude: 0.4 }, 800);
    }
    fetch(`/api/radio/place/${point.id}/channels`)
      .then(r => r.json())
      .then((ch: RadioChannel[]) => { setStations(ch); setStationsLoading(false); })
      .catch(() => setStationsLoading(false));
  };

  // ── Watch container visibility (detects tab becoming active) ──
  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    // Check immediately
    if (el.clientWidth > 0 && el.clientHeight > 0) {
      setContainerVisible(true);
    }

    const observer = new ResizeObserver((entries) => {
      for (const entry of entries) {
        const { width, height } = entry.contentRect;
        if (width > 0 && height > 0) {
          setContainerVisible(true);
        }
      }
    });
    observer.observe(el);

    return () => observer.disconnect();
  }, []);

  // ── Initialize globe ──
  useEffect(() => {
    if (!containerRef.current || places.length === 0) return;

    // If container is hidden (display:none), wait for it to become visible
    const cw = containerRef.current.clientWidth;
    const ch = containerRef.current.clientHeight;

    if (globeRef.current) {
      globeRef.current.pointsData(places);
      // Re-apply dimensions in case we were hidden during init
      if (cw > 0 && ch > 0) globeRef.current.width(cw).height(ch);
      return;
    }

    // Don't initialize globe with zero dimensions — containerVisible will re-trigger
    if (cw === 0 || ch === 0) return;

    // Read accent color from theme
    const initStyle = getComputedStyle(containerRef.current.parentElement!);
    const initRgb = initStyle.getPropertyValue('--accent-rgb').trim() || '230, 126, 34';

    const globe = new Globe(containerRef.current)
      .backgroundColor('rgba(0,0,0,0)')
      .atmosphereColor(`rgba(${initRgb}, 0.25)`)
      .atmosphereAltitude(0.12)
      .globeImageUrl('/nasa-blue-marble.jpg')
      .pointsData(places)
      // Radio Garden geo format: [lng, lat]
      .pointLat((d: any) => d.geo[1])
      .pointLng((d: any) => d.geo[0])
      .pointColor(() => `rgba(${initRgb}, 0.85)`)
      .pointRadius((d: any) => Math.max(0.12, Math.min(0.45, 0.06 + (d.size ?? 1) * 0.005)))
      .pointAltitude(0.001)
      .pointResolution(24)
      .pointLabel((d: any) =>
        `<div style="font-family:system-ui;font-size:13px;color:#fff;background:rgba(30,31,34,0.92);padding:6px 10px;border-radius:6px;border:1px solid rgba(${initRgb},0.3);pointer-events:none">` +
        `<b>${d.title}</b><br/><span style="color:#949ba4;font-size:11px">${d.country}</span></div>`
      )
      .onPointClick((d: any) => handlePointClickRef.current?.(d))
      .width(containerRef.current.clientWidth)
      .height(containerRef.current.clientHeight);

    // Sharp rendering on HiDPI/Retina displays
    globe.renderer().setPixelRatio(window.devicePixelRatio);

    // Start-Position: Europa
    globe.pointOfView({ lat: 48, lng: 10, altitude: BASE_ALT });

    // Auto-Rotation
    const controls = globe.controls() as any;
    if (controls) {
      controls.autoRotate = true;
      controls.autoRotateSpeed = 0.3;
    }

    // ── Zoom-based dot scaling ──
    // Dots scale with sqrt(altitude) so they stay visually consistent:
    // zoomed out (alt 2.0) → radius 0.18°, zoomed in (alt 0.4) → ~0.08°
    let lastAlt = BASE_ALT;
    const onControlsChange = () => {
      const pov = globe.pointOfView();
      const alt = pov.altitude;
      if (Math.abs(alt - lastAlt) / lastAlt < 0.05) return;
      lastAlt = alt;
      const scale = Math.sqrt(alt / BASE_ALT);
      globe.pointRadius((d: any) => {
        const base = Math.max(0.12, Math.min(0.45, 0.06 + (d.size ?? 1) * 0.005));
        return base * Math.max(0.15, Math.min(2.5, scale));
      });
    };
    controls.addEventListener('change', onControlsChange);

    globeRef.current = globe;

    // Pause rotation on any globe interaction (drag, scroll, touch)
    const el = containerRef.current;
    const onInteract = () => pauseRotation();
    el.addEventListener('mousedown', onInteract);
    el.addEventListener('touchstart', onInteract);
    el.addEventListener('wheel', onInteract);

    const onResize = () => {
      if (containerRef.current && globeRef.current) {
        const w = containerRef.current.clientWidth;
        const h = containerRef.current.clientHeight;
        if (w > 0 && h > 0) {
          globeRef.current.width(w).height(h);
        }
      }
    };
    window.addEventListener('resize', onResize);

    // ResizeObserver: detects when tab becomes visible (0×0 → real size)
    const resizeObserver = new ResizeObserver(() => onResize());
    resizeObserver.observe(el);

    return () => {
      controls.removeEventListener('change', onControlsChange);
      el.removeEventListener('mousedown', onInteract);
      el.removeEventListener('touchstart', onInteract);
      el.removeEventListener('wheel', onInteract);
      window.removeEventListener('resize', onResize);
      resizeObserver.disconnect();
    };
  }, [places, pauseRotation, containerVisible]);

  // ── Play handler ──
  const handlePlay = useCallback(async (
    stationId: string, stationName: string,
    overridePlaceName?: string, overrideCountry?: string,
  ) => {
    if (!selectedGuild || !selectedChannel) return;
    setPlayingLoading(true);
    try {
      const res = await fetch('/api/radio/play', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          guildId: selectedGuild,
          voiceChannelId: selectedChannel,
          stationId,
          stationName,
          placeName: overridePlaceName ?? selectedPlace?.title ?? '',
          country: overrideCountry ?? selectedPlace?.country ?? '',
        }),
      });
      const result = await res.json();
      if (result.ok) {
        setNowPlaying(prev => ({
          ...prev,
          [selectedGuild]: {
            stationId, stationName,
            placeName: overridePlaceName ?? selectedPlace?.title ?? '',
            country: overrideCountry ?? selectedPlace?.country ?? '',
            startedAt: new Date().toISOString(),
            channelName: guilds.find(g => g.id === selectedGuild)
              ?.voiceChannels.find(c => c.id === selectedChannel)?.name ?? '',
          },
        }));
        pauseRotation();
      }
    } catch (e) { console.error(e); }
    setPlayingLoading(false);
  }, [selectedGuild, selectedChannel, selectedPlace, guilds]);

  // ── Stop handler ──
  const handleStop = useCallback(async () => {
    if (!selectedGuild) return;
    await fetch('/api/radio/stop', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ guildId: selectedGuild }),
    });
    setNowPlaying(prev => {
      const next = { ...prev };
      delete next[selectedGuild];
      return next;
    });
  }, [selectedGuild]);

  // ── Search handler ──
  const handleSearch = useCallback((q: string) => {
    setSearchQuery(q);
    if (searchTimeout.current) clearTimeout(searchTimeout.current);
    if (!q.trim()) { setSearchResults([]); setSearchOpen(false); return; }
    searchTimeout.current = setTimeout(async () => {
      try {
        const res = await fetch(`/api/radio/search?q=${encodeURIComponent(q)}`);
        const results: SearchHit[] = await res.json();
        setSearchResults(results);
        setSearchOpen(true);
      } catch { setSearchResults([]); }
    }, 350);
  }, []);

  // ── Search result click ──
  const handleSearchResultClick = useCallback((hit: SearchHit) => {
    setSearchOpen(false);
    setSearchQuery('');
    setSearchResults([]);

    if (hit.type === 'channel') {
      const channelId = hit.url.match(/\/listen\/[^/]+\/([^/]+)/)?.[1];
      if (channelId) {
        handlePlay(channelId, hit.title, hit.subtitle, '');
      }
    } else if (hit.type === 'place') {
      const placeId = hit.url.match(/\/visit\/[^/]+\/([^/]+)/)?.[1];
      const place = places.find(p => p.id === placeId);
      if (place) handlePointClickRef.current?.(place);
    }
  }, [places, handlePlay]);

  // ── Favorite toggle ──
  const toggleFavorite = useCallback(async (stationId: string, stationName: string) => {
    try {
      const res = await fetch('/api/radio/favorites', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          stationId, stationName,
          placeName: selectedPlace?.title ?? '',
          country: selectedPlace?.country ?? '',
          placeId: selectedPlace?.id ?? '',
        }),
      });
      const result = await res.json();
      if (result.favorites) setFavorites(result.favorites);
    } catch {}
  }, [selectedPlace]);

  // ── Volume handler (debounced) ──
  const handleVolume = useCallback((val: number) => {
    setVolume(val);
    if (!selectedGuild) return;
    if (volumeTimeout.current) clearTimeout(volumeTimeout.current);
    volumeTimeout.current = setTimeout(() => {
      fetch('/api/radio/volume', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ guildId: selectedGuild, volume: val }),
      }).catch(console.error);
    }, 100);
  }, [selectedGuild]);

  const isFavorite = (stationId: string) => favorites.some(f => f.stationId === stationId);
  const currentPlaying = selectedGuild ? nowPlaying[selectedGuild] : null;
  const currentGuild = guilds.find(g => g.id === selectedGuild);

  return (
    <div className="radio-container" data-theme={theme}>

      {/* ═══ TOPBAR ═══ */}
      <header className="radio-topbar">
        <div className="radio-topbar-left">
          <span className="radio-topbar-logo">{'\u{1F30D}'}</span>
          <span className="radio-topbar-title">World Radio</span>

          {guilds.length > 1 && (
            <select className="radio-sel" value={selectedGuild} onChange={e => {
              setSelectedGuild(e.target.value);
              const g = guilds.find(x => x.id === e.target.value);
              const ch = g?.voiceChannels.find(c => c.members > 0) ?? g?.voiceChannels[0];
              setSelectedChannel(ch?.id ?? '');
            }}>
              {guilds.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
            </select>
          )}
          <select className="radio-sel" value={selectedChannel} onChange={e => setSelectedChannel(e.target.value)}>
            <option value="">Voice Channel...</option>
            {currentGuild?.voiceChannels.map(c => (
              <option key={c.id} value={c.id}>{'\u{1F50A}'} {c.name}{c.members > 0 ? ` (${c.members})` : ''}</option>
            ))}
          </select>
        </div>

        {currentPlaying && (
          <div className="radio-topbar-np">
            <div className="radio-eq radio-eq-np"><span /><span /><span /></div>
            <div className="radio-np-info">
              <span className="radio-np-name">{currentPlaying.stationName}</span>
              <span className="radio-np-loc">{currentPlaying.placeName}{currentPlaying.country ? `, ${currentPlaying.country}` : ''}</span>
            </div>
          </div>
        )}

        <div className="radio-topbar-right">
          {currentPlaying && (
            <>
              <div className="radio-volume">
                <span className="radio-volume-icon">{volume === 0 ? '\u{1F507}' : volume < 0.4 ? '\u{1F509}' : '\u{1F50A}'}</span>
                <input
                  type="range"
                  className="radio-volume-slider"
                  min={0} max={1} step={0.01}
                  value={volume}
                  onChange={e => handleVolume(Number(e.target.value))}
                />
                <span className="radio-volume-val">{Math.round(volume * 100)}%</span>
              </div>
              <div className="radio-conn" onClick={() => setShowConnModal(true)} title="Verbindungsdetails">
                <span className="radio-conn-dot" />
                Verbunden
                {voiceStats?.voicePing != null && (
                  <span className="radio-conn-ping">{voiceStats.voicePing}ms</span>
                )}
              </div>
              <button className="radio-topbar-stop" onClick={handleStop}>{'\u23F9'} Stop</button>
            </>
          )}
          <div className="radio-theme-inline">
            {THEMES.map(t => (
              <div
                key={t.id}
                className={`radio-theme-dot ${theme === t.id ? 'active' : ''}`}
                style={{ background: t.color }}
                title={t.label}
                onClick={() => setTheme(t.id)}
              />
            ))}
          </div>
        </div>
      </header>

      {/* ═══ GLOBE AREA ═══ */}
      <div className="radio-globe-wrap">
        <div className="radio-globe" ref={containerRef} />

        {/* ── Search ── */}
        <div className="radio-search">
          <div className="radio-search-wrap">
            <span className="radio-search-icon">{'\u{1F50D}'}</span>
            <input
              className="radio-search-input"
              type="text"
              placeholder="Sender oder Stadt suchen..."
              value={searchQuery}
              onChange={e => handleSearch(e.target.value)}
              onFocus={() => { if (searchResults.length) setSearchOpen(true); }}
            />
            {searchQuery && (
              <button className="radio-search-clear" onClick={() => { setSearchQuery(''); setSearchResults([]); setSearchOpen(false); }}>{'\u2715'}</button>
            )}
          </div>
          {searchOpen && searchResults.length > 0 && (
            <div className="radio-search-results">
              {searchResults.slice(0, 12).map(hit => (
                <button key={hit.id + hit.url} className="radio-search-result" onClick={() => handleSearchResultClick(hit)}>
                  <span className="radio-search-result-icon">
                    {hit.type === 'channel' ? '\u{1F4FB}' : hit.type === 'place' ? '\u{1F4CD}' : '\u{1F30D}'}
                  </span>
                  <div className="radio-search-result-text">
                    <span className="radio-search-result-title">{hit.title}</span>
                    <span className="radio-search-result-sub">{hit.subtitle}</span>
                  </div>
                </button>
              ))}
            </div>
          )}
        </div>

        {/* ── Favorites toggle (hidden when any panel is open) ── */}
        {!selectedPlace && !showFavorites && (
          <button
            className="radio-fab"
            onClick={() => { setShowFavorites(true); setSelectedPlace(null); }}
            title="Favoriten"
          >
            {'\u2B50'}{favorites.length > 0 && <span className="radio-fab-badge">{favorites.length}</span>}
          </button>
        )}

        {/* ── Side Panel: Favorites ── */}
        {showFavorites && (
          <div className="radio-panel open">
            <div className="radio-panel-header">
              <h3>{'\u2B50'} Favoriten</h3>
              <button className="radio-panel-close" onClick={() => setShowFavorites(false)}>{'\u2715'}</button>
            </div>
            <div className="radio-panel-body">
              {favorites.length === 0 ? (
                <div className="radio-panel-empty">Noch keine Favoriten</div>
              ) : (
                favorites.map(fav => (
                  <div key={fav.stationId} className={`radio-station ${currentPlaying?.stationId === fav.stationId ? 'playing' : ''}`}>
                    <div className="radio-station-info">
                      <span className="radio-station-name">{fav.stationName}</span>
                      <span className="radio-station-loc">{fav.placeName}, {fav.country}</span>
                    </div>
                    <div className="radio-station-btns">
                      <button
                        className="radio-btn-play"
                        onClick={() => handlePlay(fav.stationId, fav.stationName, fav.placeName, fav.country)}
                        disabled={!selectedChannel || playingLoading}
                      >{'\u25B6'}</button>
                      <button className="radio-btn-fav active" onClick={() => toggleFavorite(fav.stationId, fav.stationName)}>{'\u2605'}</button>
                    </div>
                  </div>
                ))
              )}
            </div>
          </div>
        )}

        {/* ── Side Panel: Stations at place ── */}
        {selectedPlace && !showFavorites && (
          <div className="radio-panel open">
            <div className="radio-panel-header">
              <div>
                <h3>{selectedPlace.title}</h3>
                <span className="radio-panel-sub">{selectedPlace.country}</span>
              </div>
              <button className="radio-panel-close" onClick={() => setSelectedPlace(null)}>{'\u2715'}</button>
            </div>
            <div className="radio-panel-body">
              {stationsLoading ? (
                <div className="radio-panel-loading">
                  <div className="radio-spinner" />
                  Sender werden geladen...
                </div>
              ) : stations.length === 0 ? (
                <div className="radio-panel-empty">Keine Sender gefunden</div>
              ) : (
                stations.map(s => (
                  <div key={s.id} className={`radio-station ${currentPlaying?.stationId === s.id ? 'playing' : ''}`}>
                    <div className="radio-station-info">
                      <span className="radio-station-name">{s.title}</span>
                      {currentPlaying?.stationId === s.id && (
                        <span className="radio-station-live">
                          <span className="radio-eq"><span /><span /><span /></span>
                          Live
                        </span>
                      )}
                    </div>
                    <div className="radio-station-btns">
                      {currentPlaying?.stationId === s.id ? (
                        <button className="radio-btn-stop" onClick={handleStop}>{'\u23F9'}</button>
                      ) : (
                        <button
                          className="radio-btn-play"
                          onClick={() => handlePlay(s.id, s.title)}
                          disabled={!selectedChannel || playingLoading}
                        >{'\u25B6'}</button>
                      )}
                      <button
                        className={`radio-btn-fav ${isFavorite(s.id) ? 'active' : ''}`}
                        onClick={() => toggleFavorite(s.id, s.title)}
                      >{isFavorite(s.id) ? '\u2605' : '\u2606'}</button>
                    </div>
                  </div>
                ))
              )}
            </div>
          </div>
        )}

        {/* ── Places counter ── */}
        <div className="radio-counter">
          {'\u{1F4FB}'} {places.length.toLocaleString('de-DE')} Sender weltweit
        </div>
        <a
          className="radio-attribution"
          href="https://science.nasa.gov/earth/earth-observatory/blue-marble-next-generation/"
          target="_blank"
          rel="noreferrer"
        >
          Imagery © NASA Blue Marble
        </a>
      </div>

      {/* ── Connection Details Modal ── */}
      {showConnModal && (() => {
        const uptimeSec = voiceStats?.connectedSince
          ? Math.floor((Date.now() - new Date(voiceStats.connectedSince).getTime()) / 1000)
          : 0;
        const h = Math.floor(uptimeSec / 3600);
        const m = Math.floor((uptimeSec % 3600) / 60);
        const s = uptimeSec % 60;
        const uptimeStr = h > 0
          ? `${h}h ${String(m).padStart(2, '0')}m ${String(s).padStart(2, '0')}s`
          : m > 0
            ? `${m}m ${String(s).padStart(2, '0')}s`
            : `${s}s`;
        const pingColor = (ms: number | null) =>
          ms == null ? 'var(--text-faint)' : ms < 80 ? 'var(--success)' : ms < 150 ? '#f0a830' : '#e04040';
        return (
          <div className="radio-modal-overlay" onClick={() => setShowConnModal(false)}>
            <div className="radio-modal" onClick={e => e.stopPropagation()}>
              <div className="radio-modal-header">
                <span>{'\u{1F4E1}'}</span>
                <span>Verbindungsdetails</span>
                <button className="radio-modal-close" onClick={() => setShowConnModal(false)}>{'\u2715'}</button>
              </div>
              <div className="radio-modal-body">
                <div className="radio-modal-stat">
                  <span className="radio-modal-label">Voice Ping</span>
                  <span className="radio-modal-value">
                    <span className="radio-modal-dot" style={{ background: pingColor(voiceStats?.voicePing ?? null) }} />
                    {voiceStats?.voicePing != null ? `${voiceStats.voicePing} ms` : '---'}
                  </span>
                </div>
                <div className="radio-modal-stat">
                  <span className="radio-modal-label">Gateway Ping</span>
                  <span className="radio-modal-value">
                    <span className="radio-modal-dot" style={{ background: pingColor(voiceStats?.gatewayPing ?? null) }} />
                    {voiceStats && voiceStats.gatewayPing >= 0 ? `${voiceStats.gatewayPing} ms` : '---'}
                  </span>
                </div>
                <div className="radio-modal-stat">
                  <span className="radio-modal-label">Status</span>
                  <span className="radio-modal-value" style={{ color: voiceStats?.status === 'ready' ? 'var(--success)' : '#f0a830' }}>
                    {voiceStats?.status === 'ready' ? 'Verbunden' : voiceStats?.status ?? 'Warte auf Verbindung'}
                  </span>
                </div>
                <div className="radio-modal-stat">
                  <span className="radio-modal-label">Kanal</span>
                  <span className="radio-modal-value">{voiceStats?.channelName || '---'}</span>
                </div>
                <div className="radio-modal-stat">
                  <span className="radio-modal-label">Verbunden seit</span>
                  <span className="radio-modal-value">{uptimeStr || '---'}</span>
                </div>
              </div>
            </div>
          </div>
        );
      })()}
    </div>
  );
}
