// Vintage tabletop radio receiver.
// Horizontal dial with backlit cellophane, sliding needle, FM frequencies.
const { useState: useStateR, useEffect: useEffectR, useRef: useRefR, useCallback } = React;

const FM_MIN = 87.5;
const FM_MAX = 108.5;

function dialPct(freq) {
  return ((freq - FM_MIN) / (FM_MAX - FM_MIN)) * 100;
}

function RadioDial({ freq, onSeek, langFilter = "all" }) {
  const dialRef = useRefR(null);
  const dragging = useRefR(false);

  function pointerToFreq(clientX) {
    const r = dialRef.current.getBoundingClientRect();
    const pct = Math.max(0, Math.min(1, (clientX - r.left) / r.width));
    return FM_MIN + pct * (FM_MAX - FM_MIN);
  }

  function onDown(e) {
    dragging.current = true;
    onSeek(pointerToFreq(e.clientX));
    e.currentTarget.setPointerCapture?.(e.pointerId);
  }
  function onMove(e) {
    if (!dragging.current) return;
    onSeek(pointerToFreq(e.clientX));
  }
  function onUp() { dragging.current = false; }

  // Major tick every 1 MHz, minor every 0.2 MHz.
  const ticks = [];
  for (let f = Math.ceil(FM_MIN); f <= FM_MAX; f += 1) {
    ticks.push({ f, major: true });
  }
  for (let f = FM_MIN; f <= FM_MAX; f += 0.2) {
    if (Math.abs(f - Math.round(f)) > 0.05) ticks.push({ f, major: false });
  }

  return (
    <div
      className="radio-dial"
      ref={dialRef}
      onPointerDown={onDown}
      onPointerMove={onMove}
      onPointerUp={onUp}
      onPointerCancel={onUp}
    >
      <div className="radio-dial-glass">
        <div className="radio-dial-band-label radio-dial-band-fm">FM · MHz</div>
        <div className="radio-dial-band-label radio-dial-band-am">SW · kHz</div>

        <div className="radio-dial-scale">
          {ticks.map((t, i) => (
            <div
              key={i}
              className={`radio-tick ${t.major ? "radio-tick-major" : "radio-tick-minor"}`}
              style={{ left: `${dialPct(t.f)}%` }}
            >
              {t.major && <span className="radio-tick-num">{t.f}</span>}
            </div>
          ))}
        </div>

        <div className="radio-station-row">
          {RADIO_STATIONS.map(s => {
            const dimmed = langFilter !== "all" && s.lang !== langFilter;
            return (
              <div
                key={s.freq}
                className={`radio-station-mark ${dimmed ? "dimmed" : ""}`}
                style={{ left: `${dialPct(s.freq)}%` }}
                onClick={(e) => { e.stopPropagation(); onSeek(s.freq); }}
                title={`${s.name}${dimmed ? " (outside language filter)" : ""}`}
              >
                <div className="radio-station-pip" />
                <div className="radio-station-label">{s.name}</div>
              </div>
            );
          })}
        </div>

        <div className="radio-needle" style={{ left: `${dialPct(freq)}%` }}>
          <div className="radio-needle-line" />
          <div className="radio-needle-head" />
        </div>
      </div>
    </div>
  );
}

function VuMeter({ level }) {
  // 7-bar VU. Active count derived from level [0..1].
  const bars = 9;
  const active = Math.round(level * bars);
  return (
    <div className="radio-vu">
      {Array.from({ length: bars }).map((_, i) => (
        <div
          key={i}
          className={`radio-vu-bar ${i < active ? "on" : ""} ${i >= bars - 2 ? "hot" : ""}`}
        />
      ))}
    </div>
  );
}

function RadioSet({ skin = "walnut", langFilter = "all" }) {
  const [freq, setFreq] = useStateR(100.5);
  const [vol, setVol] = useStateR(0.6);
  const [power, setPower] = useStateR(true);
  const audioRef = useRefR(null);
  const hlsRef = useRefR(null);

  // Active stations under the current language filter. The dial keeps
  // ALL station pips visible (dimmed outside the filter, see RadioDial),
  // but "nearest" / preset cycling only considers matching ones.
  const filtered = langFilter === "all"
    ? RADIO_STATIONS
    : RADIO_STATIONS.filter(s => s.lang === langFilter);
  const effective = filtered.length > 0 ? filtered : RADIO_STATIONS;

  // Determine current station: nearest within 0.3 MHz tolerance, biased
  // toward the active filter.
  const nearest = effective.reduce((a, b) =>
    Math.abs(b.freq - freq) < Math.abs(a.freq - freq) ? b : a, effective[0]);
  const tuned = power && Math.abs(nearest.freq - freq) < 0.3;

  // When the user flips the filter, snap the needle to the first station
  // in the new language so they hear something immediately.
  useEffectR(() => {
    if (langFilter === "all") return;
    if (filtered.length === 0) return;
    setFreq(filtered[0].freq);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [langFilter]);
  const offset = Math.abs(nearest.freq - freq);

  // Signal strength falls off the further off-frequency.
  const signal = !power ? 0 : Math.max(0, 1 - offset / 0.3) * 0.85 + (tuned ? 0.15 : 0);

  // Animate VU between bars when tuned.
  const [vu, setVu] = useStateR(0.4);
  useEffectR(() => {
    if (!tuned) { setVu(signal * 0.3); return; }
    const id = setInterval(() => setVu(0.45 + Math.random() * 0.4), 180);
    return () => clearInterval(id);
  }, [tuned, signal]);

  // Wire the <audio> element to the tuned station. MP3 streams plug in
  // directly via audio.src. HLS streams attach via hls.js (Safari handles
  // HLS natively in <audio>, but Chrome/Firefox need hls.js).
  useEffectR(() => {
    const audio = audioRef.current;
    if (!audio) return;
    // Tear down previous hls.js instance.
    if (hlsRef.current) { hlsRef.current.destroy(); hlsRef.current = null; }
    if (!power || !tuned) {
      audio.pause();
      audio.removeAttribute("src");
      audio.load();
      return;
    }
    // Direct broadcaster URL — no Worker proxy, no egress cost. Stations
    // whose CDN refuses the browser origin (some BBC sub-streams, NHK
    // domestic, certain MP3 icecasts) will simply not play; the listener
    // can pick another preset.
    const url = nearest.streamUrl;
    if (nearest.streamFormat === "hls" && !audio.canPlayType("application/vnd.apple.mpegurl")) {
      if (window.Hls && window.Hls.isSupported()) {
        const hls = new window.Hls();
        hls.loadSource(url);
        hls.attachMedia(audio);
        hlsRef.current = hls;
      } else {
        console.warn("HLS unsupported for", nearest.name);
      }
    } else {
      audio.src = url;
    }
    audio.play().catch(() => {
      // Autoplay was blocked. The user can hit the power preset again to
      // retry inside a user-gesture handler, or unmute via OS controls.
    });
  }, [power, tuned, nearest.streamUrl, nearest.streamFormat]);

  // Apply volume + mute power.
  useEffectR(() => {
    if (audioRef.current) audioRef.current.volume = power ? vol : 0;
  }, [vol, power]);

  function nudge(dir) {
    setFreq(f => Math.max(FM_MIN, Math.min(FM_MAX, +(f + dir * 0.1).toFixed(1))));
  }

  function preset(s) { setFreq(s.freq); }

  useEffectR(() => {
    function onKey(e) {
      const tag = (e.target && e.target.tagName || "").toLowerCase();
      if (tag === "input" || tag === "textarea" || (e.target && e.target.isContentEditable)) return;
      const isArrow = e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "ArrowLeft" || e.key === "ArrowRight";
      if (isArrow) e.preventDefault();
      if (e.key === "ArrowRight") nudge(1);
      if (e.key === "ArrowLeft") nudge(-1);
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  return (
    <div className={`radio-set radio-skin-${skin} ${power ? "" : "radio-off"}`}>
      <audio ref={audioRef} preload="none" crossOrigin="anonymous" />
      <div className="radio-cabinet">
        <div className="radio-top-row">
          <div className="radio-brand">
            <div className="radio-brand-name">CHANNELROAM</div>
            <div className="radio-brand-model">MODEL R-2026 · SOLID-STATE</div>
          </div>
          <div className="radio-band-switch">
            <div className="radio-band-pill radio-band-pill-on">FM</div>
            <div className="radio-band-pill">MW</div>
            <div className="radio-band-pill">SW</div>
          </div>
        </div>

        <RadioDial freq={freq} onSeek={(f) => setFreq(+f.toFixed(2))} langFilter={langFilter} />

        <div className="radio-readout-row">
          <div className="radio-readout">
            <div className="radio-readout-freq">{freq.toFixed(1)}<span className="radio-readout-unit">MHz</span></div>
            <div className="radio-readout-station">
              {power
                ? (tuned ? nearest.name : "— TUNING —")
                : "— STANDBY —"}
            </div>
            <div className="radio-readout-now">
              {power && tuned
                ? `${nearest.publisher} · ${nearest.region} · ${(nearest.streamFormat || "").toUpperCase()}`
                : (power ? "static · adjust dial" : "")}
            </div>
          </div>
          <div className="radio-vu-block">
            <div className="radio-vu-label">SIGNAL</div>
            <VuMeter level={vu} />
            <div className="radio-stereo">{tuned ? "● STEREO" : "○ MONO"}</div>
          </div>
        </div>

        <div className="radio-grille-row">
          <div className="radio-knob-cluster">
            <div className="radio-knob"
                 style={{ "--rot": `${-135 + dialPct(freq) / 100 * 270}deg` }}
                 onWheel={(e) => { e.preventDefault(); nudge(e.deltaY < 0 ? 1 : -1); }}>
              <div className="radio-knob-dial" />
              <div className="radio-knob-pointer" />
            </div>
            <div className="radio-knob-label">TUNING</div>
          </div>

          <div className="radio-grille">
            <div className="radio-grille-cloth" />
            <div className="radio-grille-emblem">◐</div>
          </div>

          <div className="radio-knob-cluster">
            <div className="radio-knob"
                 style={{ "--rot": `${-135 + vol * 270}deg` }}
                 onWheel={(e) => { e.preventDefault(); setVol(v => Math.max(0, Math.min(1, v + (e.deltaY < 0 ? 0.05 : -0.05)))); }}>
              <div className="radio-knob-dial" />
              <div className="radio-knob-pointer" />
            </div>
            <div className="radio-knob-label">VOLUME</div>
          </div>
        </div>

        <div className="radio-preset-row">
          {effective.slice(0, 7).map((s, i) => (
            <button
              key={s.freq}
              className={`radio-preset ${Math.abs(s.freq - freq) < 0.05 ? "on" : ""}`}
              onClick={() => preset(s)}
            >
              <div className="radio-preset-num">{i + 1}</div>
              <div className="radio-preset-name">{s.name.split(" ")[0]}</div>
            </button>
          ))}
          <button
            className={`radio-preset radio-preset-power ${power ? "on" : ""}`}
            onClick={() => setPower(p => !p)}
          >
            <div className="radio-preset-num">⏻</div>
            <div className="radio-preset-name">{power ? "ON" : "OFF"}</div>
          </button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { RadioSet });
