import { useEffect, useRef, useState } from 'react';
import { Radio, Zap, Power, AudioLines, Flame, Gauge, Volume2, VolumeX } from 'lucide-react';
import {
  GetFlexState, FlexSetPower, FlexSetTunePower, FlexTune, FlexSetVox, FlexSetVoxLevel, FlexSetVoxDelay,
  FlexSetProcessor, FlexSetProcessorLevel, FlexSetMon, FlexSetMonLevel, FlexSetMic,
  FlexMox, FlexAmpOperate,
  GetPGXLStatus, PGXLSetFanMode,
  FlexSetAGCMode, FlexSetAGCThreshold, FlexSetAudioLevel, FlexSetMute, FlexSetRXAntenna, FlexSetTXAntenna, FlexSetSplit,
  FlexSetNB, FlexSetNBLevel, FlexSetNR, FlexSetNRLevel, FlexSetANF, FlexSetANFLevel,
  FlexSetAPF, FlexSetAPFLevel, FlexSetCWSpeed, FlexSetCWPitch, FlexSetCWBreakInDelay,
  FlexSetCWSidetone, FlexSetSidetoneLevel, FlexSetCWFilter, FlexSetFilter,
} from '../../wailsjs/go/main/App';
import { cn } from '@/lib/utils';

type FlexState = {
  available: boolean; model?: string;
  rf_power: number; tune_power: number; tune: boolean; transmitting: boolean;
  vox_enable: boolean; vox_level: number; vox_delay: number;
  proc_enable: boolean; proc_level: number;
  mon: boolean; mon_level: number; mic_level: number;
  atu_status?: string; atu_memories: boolean;
  rx_avail: boolean; agc_mode?: string; agc_threshold: number; audio_level: number; mute: boolean;
  rx_ant?: string; tx_ant?: string; ant_list?: string[]; tx_ant_list?: string[];
  split: boolean; rx_freq_hz?: number; tx_freq_hz?: number;
  nb: boolean; nb_level: number; nr: boolean; nr_level: number; anf: boolean; anf_level: number;
  mode?: string;
  cw_speed: number; cw_pitch: number; cw_break_in_delay: number; cw_sidetone: boolean; cw_mon_level: number;
  apf: boolean; apf_level: number; filter_lo: number; filter_hi: number;
  amp_available: boolean; amp_model?: string; amp_operate: boolean; amp_fault?: string;
  meters?: Meter[];
};

type Meter = { id: number; src?: string; name?: string; unit?: string; value: number; lo: number; hi: number };

const ZERO: FlexState = {
  available: false, rf_power: 0, tune_power: 0, tune: false, transmitting: false,
  vox_enable: false, vox_level: 0, vox_delay: 0, proc_enable: false, proc_level: 0,
  mon: false, mon_level: 0, mic_level: 0, atu_memories: false,
  rx_avail: false, agc_threshold: 0, audio_level: 0, mute: false, split: false,
  nb: false, nb_level: 0, nr: false, nr_level: 0, anf: false, anf_level: 0,
  cw_speed: 25, cw_pitch: 600, cw_break_in_delay: 30, cw_sidetone: true, cw_mon_level: 0,
  apf: false, apf_level: 0, filter_lo: 0, filter_hi: 0,
  amp_available: false, amp_operate: false,
};

function Slider({ value, onChange, disabled, accent = '#16a34a', step = 1, max = 100 }: {
  value: number; onChange: (v: number) => void; disabled?: boolean; accent?: string; step?: number; max?: number;
}) {
  const v = Math.max(0, Math.min(max, value));
  const pct = max > 0 ? (v / max) * 100 : 0;
  const ref = useRef<HTMLInputElement>(null);
  // Mouse-wheel adjusts the slider. React's onWheel is passive (preventDefault
  // is ignored), so attach a non-passive native listener; read live values via
  // refs to avoid stale closures.
  const valRef = useRef(value); valRef.current = value;
  const cbRef = useRef(onChange); cbRef.current = onChange;
  const disRef = useRef(disabled); disRef.current = disabled;
  const stepRef = useRef(step); stepRef.current = step;
  const maxRef = useRef(max); maxRef.current = max;
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onWheel = (e: WheelEvent) => {
      if (disRef.current) return;
      e.preventDefault();
      const d = e.deltaY < 0 ? stepRef.current : -stepRef.current;
      const nv = Math.max(0, Math.min(maxRef.current, valRef.current + d));
      if (nv !== valRef.current) cbRef.current(nv);
    };
    el.addEventListener('wheel', onWheel, { passive: false });
    return () => el.removeEventListener('wheel', onWheel);
  }, []);
  return (
    <input
      ref={ref}
      type="range" min={0} max={max} value={v} disabled={disabled}
      onChange={(e) => onChange(parseInt(e.target.value, 10))}
      className={cn('flex-1 h-1.5 rounded-full appearance-none cursor-pointer disabled:opacity-30 disabled:cursor-default',
        '[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-3.5 [&::-webkit-slider-thumb]:rounded-full',
        '[&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:shadow-sm [&::-webkit-slider-thumb]:cursor-pointer')}
      style={{ background: `linear-gradient(to right, ${accent} ${pct}%, #d8cfb8 ${pct}%)`, borderColor: accent }}
    />
  );
}

// Segmented — radio-style multi-choice (AGC, Processor preset).
function Segmented({ value, options, onChange, disabled }: {
  value: string; options: { v: string; l: string }[]; onChange: (v: string) => void; disabled?: boolean;
}) {
  return (
    <div className="inline-flex rounded-md border border-border overflow-hidden shrink-0">
      {options.map((o) => (
        <button key={o.v} type="button" disabled={disabled} onClick={() => onChange(o.v)}
          className={cn('px-2 py-1 text-[11px] font-bold tracking-wide transition-colors disabled:opacity-30 border-l border-border first:border-l-0',
            value === o.v ? 'bg-primary text-primary-foreground' : 'bg-card text-muted-foreground hover:bg-muted')}>
          {o.l}
        </button>
      ))}
    </div>
  );
}

// Chip — a compact on/off pill (NB/NR/ANF/VOX/MON…).
function Chip({ on, onClick, label, disabled, accent = 'emerald' }: {
  on: boolean; onClick: () => void; label: string; disabled?: boolean; accent?: 'emerald' | 'violet' | 'cyan' | 'amber';
}) {
  const onCls = {
    emerald: 'bg-emerald-600 border-emerald-600 text-white',
    violet: 'bg-violet-600 border-violet-600 text-white',
    cyan: 'bg-cyan-600 border-cyan-600 text-white',
    amber: 'bg-amber-500 border-amber-500 text-white',
  }[accent];
  return (
    <button type="button" onClick={onClick} disabled={disabled}
      className={cn(
        // min width (not fixed) so a longer label like STONE keeps symmetric
        // padding instead of overflowing; short labels (NB/NR/APF) stay aligned.
        'min-w-[3.5rem] shrink-0 px-2.5 py-1 rounded-md text-[11px] font-bold border text-center tracking-wide transition-all disabled:opacity-30',
        on ? cn(onCls, 'shadow-sm') : 'bg-card text-muted-foreground border-border hover:bg-muted hover:border-muted-foreground/30')}>
      {label}
    </button>
  );
}

function LevelRow({ label, on, onToggle, value, onLevel, disabled, accent, sliderAccent }: {
  label: string; on: boolean; onToggle: () => void; value: number; onLevel: (v: number) => void;
  disabled?: boolean; accent?: 'emerald' | 'violet' | 'cyan' | 'amber'; sliderAccent?: string;
}) {
  return (
    <div className="flex items-center gap-2">
      <Chip on={on} onClick={onToggle} label={label} disabled={disabled} accent={accent} />
      <Slider value={value} disabled={disabled || !on} accent={sliderAccent} onChange={onLevel} />
      <span className="w-8 text-right text-xs font-mono tabular-nums text-muted-foreground">{value}</span>
    </div>
  );
}

// MeterBar — a segmented "LED" instrument bar (radio look) scaled by lo/hi.
// `display` overrides the numeric readout; `segColor` colours segments by their
// 0..1 position (zones); the top ~18% light red by default (overload/peak).
const METER_SEGMENTS = 26;
function MeterBar({ label, value, unit, lo, hi, accent = '#16a34a', extra, display, segColor }: {
  label: string; value: number; unit?: string; lo: number; hi: number; accent?: string; extra?: string; display?: string;
  segColor?: (frac: number) => string;
}) {
  const span = hi - lo;
  const pct = span > 0 ? Math.max(0, Math.min(100, ((value - lo) / span) * 100)) : 0;
  const lit = Math.round((pct / 100) * METER_SEGMENTS);
  return (
    <div className="rounded-lg border border-border/70 px-2.5 py-2 bg-gradient-to-b from-card to-muted/40 shadow-sm min-w-0">
      <div className="flex items-baseline justify-between gap-1 mb-1.5">
        <span className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground truncate">{label}</span>
        <span className="text-sm font-mono font-bold tabular-nums whitespace-nowrap text-foreground/90">
          {display !== undefined ? display : (
            <>{Math.abs(value) >= 100 ? value.toFixed(0) : value.toFixed(1)}<span className="text-muted-foreground text-[10px] ml-0.5">{unit}</span></>
          )}
        </span>
      </div>
      {/* LED bar — recessed track + gradient segments for a cleaner instrument look. */}
      <div className="flex gap-[2px] h-3 items-stretch rounded-[3px] bg-black/10 p-[2px]">
        {Array.from({ length: METER_SEGMENTS }).map((_, i) => {
          const on = i < lit;
          const frac = i / METER_SEGMENTS;
          const col = segColor ? segColor(frac) : (frac > 0.82 ? '#dc2626' : accent);
          return (
            <div key={i} className="flex-1 rounded-[2px] transition-colors duration-100"
              style={on
                ? { background: `linear-gradient(to bottom, ${col}, ${col}cc)`, boxShadow: `0 0 4px ${col}88` }
                : { background: '#cfc6ad', opacity: 0.35 }} />
          );
        })}
      </div>
      {extra && <div className="text-[10px] text-muted-foreground/70 mt-1 text-right font-mono">{extra}</div>}
    </div>
  );
}

function Card({ icon: Icon, title, accent, children }: { icon: any; title: string; accent?: string; children: React.ReactNode }) {
  return (
    <div className="rounded-xl border border-border bg-card shadow-sm overflow-hidden">
      <div className="flex items-center gap-2 px-3 py-2 border-b border-border/60 bg-muted/30">
        <Icon className="size-4" style={{ color: accent ?? 'var(--primary)' }} />
        <span className="text-xs font-bold uppercase tracking-wider text-foreground/80">{title}</span>
      </div>
      <div className="p-3 space-y-3">{children}</div>
    </div>
  );
}

// onCWSpeed (optional): notified when the operator changes CW speed here, so the
// host can keep the WinKeyer (which actually sends the macros) in sync.
export function FlexPanel({ onCWSpeed }: { onCWSpeed?: (wpm: number) => void } = {}) {
  const [st, setSt] = useState<FlexState>(ZERO);
  const hold = useRef<Record<string, number>>({});
  // Peak-hold: keep the highest reading for ~2 s so the jittery VITA-49 meters
  // read steadily instead of jumping every poll.
  const peak = useRef<Record<string, { v: number; t: number }>>({});
  const peakHold = (key: string, val: number) => {
    const now = Date.now();
    const p = peak.current[key];
    if (!p || val >= p.v || now - p.t > 2000) { peak.current[key] = { v: val, t: now }; return val; }
    return p.v;
  };

  useEffect(() => {
    let alive = true;
    const tick = async () => {
      try {
        const s = (await GetFlexState()) as any as FlexState;
        if (!alive) return;
        setSt((prev) => {
          const now = Date.now();
          const merged: any = { ...s };
          for (const k in hold.current) {
            if (hold.current[k] > now) merged[k] = (prev as any)[k];
          }
          return merged as FlexState;
        });
      } catch { /* not connected */ }
    };
    tick();
    const id = window.setInterval(tick, 400);
    return () => { alive = false; window.clearInterval(id); };
  }, []);

  // PowerGenius XL direct connection (fan mode), independent of the Flex link.
  const [pg, setPg] = useState<{ connected: boolean; fan_mode?: string; host?: string; last_error?: string }>({ connected: false });
  useEffect(() => {
    let alive = true;
    const tick = async () => { try { const s: any = await GetPGXLStatus(); if (alive) setPg(s || { connected: false }); } catch {} };
    tick();
    const id = window.setInterval(tick, 2000);
    return () => { alive = false; window.clearInterval(id); };
  }, []);

  const change = (key: keyof FlexState, val: number | boolean | string, send: () => Promise<any>) => {
    hold.current[key] = Date.now() + 900;
    setSt((p) => ({ ...p, [key]: val }));
    send().catch(() => {});
  };

  const off = !st.available;
  const rxOff = off || !st.rx_avail;
  const isCW = (st.mode || '').toUpperCase().includes('CW');
  const PROC = [{ v: '0', l: 'NOR' }, { v: '1', l: 'DX' }, { v: '2', l: 'DX+' }];
  const AGC = [{ v: 'off', l: 'OFF' }, { v: 'slow', l: 'SLOW' }, { v: 'med', l: 'MED' }, { v: 'fast', l: 'FAST' }];
  const CW_BW = [100, 200, 300, 400, 500];
  const SSB_BW = [1800, 2100, 2400, 2700, 3000, 4000, 6000];
  const curBW = Math.max(0, (st.filter_hi || 0) - (st.filter_lo || 0));
  // Highlight the preset CLOSEST to the radio's actual filter width. The rig
  // rarely reports a width that lands exactly on a preset (e.g. 2.7k presets as
  // 100–2790), so an exact/±50 match would leave nothing lit — "doesn't pick up
  // the current filter". Snapping to the nearest preset always reflects the rig.
  const ssbActiveBW = curBW > 0
    ? SSB_BW.reduce((best, bw) => (Math.abs(bw - curBW) < Math.abs(best - curBW) ? bw : best), SSB_BW[0])
    : -1;

  return (
    <div className="h-full min-h-0 overflow-auto bg-background">
      <div className="max-w-5xl mx-auto p-3 space-y-3">
        {/* Header strip */}
        <div className="flex items-center gap-3 rounded-xl px-4 py-3 text-white shadow-sm"
          style={{ background: 'linear-gradient(135deg,#1e293b,#0f172a)' }}>
          <Radio className="size-6 text-sky-400" />
          <div className="flex flex-col leading-tight">
            <span className="text-base font-extrabold tracking-tight">{st.model || 'FlexRadio'}</span>
            <span className="text-[11px] text-slate-400">SmartSDR remote control</span>
          </div>
          <div className="flex-1" />
          <span className={cn('inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-extrabold tracking-wider',
            !st.available ? 'bg-slate-700 text-slate-300'
              : st.transmitting ? 'bg-rose-500 text-white shadow-[0_0_16px] shadow-rose-500/60' : 'bg-emerald-500 text-white')}>
            <span className="size-2 rounded-full bg-current" />
            {!st.available ? 'OFFLINE' : st.transmitting ? 'TX' : 'RX'}
          </span>
        </div>

        {off && (
          <div className="text-center text-sm text-muted-foreground py-6">
            Waiting for the FlexRadio… (set CAT to FlexRadio and connect)
          </div>
        )}

        {/* TX + RX columns */}
        <div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
          {/* TRANSMIT */}
          <Card icon={Zap} title="Transmit" accent="#dc2626">
            <div className="flex items-center gap-3">
              <span className="w-20 shrink-0 text-xs font-medium text-muted-foreground">RF Power</span>
              <Slider value={st.rf_power} disabled={off} accent="#dc2626" onChange={(v) => change('rf_power', v, () => FlexSetPower(v))} />
              <span className="w-9 text-right text-sm font-mono font-bold tabular-nums">{st.rf_power}</span>
            </div>
            <div className="flex items-center gap-3">
              <span className="w-20 shrink-0 text-xs font-medium text-muted-foreground">Tune Pwr</span>
              <Slider value={st.tune_power} disabled={off} accent="#d97706" onChange={(v) => change('tune_power', v, () => FlexSetTunePower(v))} />
              <span className="w-9 text-right text-sm font-mono font-bold tabular-nums">{st.tune_power}</span>
            </div>
            <div className="flex items-center gap-2 pt-0.5">
              <button type="button" disabled={off}
                onClick={() => change('tune', !st.tune, () => FlexTune(!st.tune))}
                className={cn('flex-1 px-3 py-2.5 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
                  st.tune ? 'bg-amber-500 text-white border-amber-500 shadow-[0_0_14px] shadow-amber-500/50' : 'bg-card text-amber-700 border-amber-400 hover:bg-amber-50')}>
                TUNE
              </button>
              <button type="button" disabled={off}
                onClick={() => change('transmitting', !st.transmitting, () => FlexMox(!st.transmitting))}
                className={cn('flex-1 px-3 py-2.5 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
                  st.transmitting ? 'bg-rose-600 text-white border-rose-600 shadow-[0_0_14px] shadow-rose-600/50' : 'bg-card text-rose-700 border-rose-400 hover:bg-rose-50')}>
                <Power className="size-4 inline mr-1 -mt-0.5" /> MOX
              </button>
            </div>
            <div className="flex items-center gap-2">
              <button type="button" disabled={off}
                title="Split: RX/TX on separate slices. ON creates a TX slice +1 kHz (CW) / +5 kHz (SSB) up, like SmartSDR."
                onClick={() => change('split', !st.split, () => FlexSetSplit(!st.split))}
                className={cn('px-3 py-1.5 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
                  st.split ? 'bg-sky-600 text-white border-sky-600 shadow-[0_0_12px] shadow-sky-600/50' : 'bg-card text-sky-700 border-sky-400 hover:bg-sky-50')}>
                SPLIT
              </button>
              {st.split && !!st.tx_freq_hz && (
                <span className="text-[11px] font-mono text-muted-foreground whitespace-nowrap">
                  TX {(st.tx_freq_hz / 1e6).toFixed(3)}
                  {!!st.rx_freq_hz && ` (${(st.tx_freq_hz - st.rx_freq_hz) >= 0 ? '+' : ''}${((st.tx_freq_hz - st.rx_freq_hz) / 1000).toFixed(1)} kHz)`}
                </span>
              )}
            </div>

            {!isCW ? (
            <div className="border-t border-border/60 pt-3 space-y-3">
              <div className="flex items-center gap-2">
                <Chip on={st.proc_enable} disabled={off} label="PROC" accent="violet"
                  onClick={() => change('proc_enable', !st.proc_enable, () => FlexSetProcessor(!st.proc_enable))} />
                <Segmented value={String(st.proc_level)} options={PROC} disabled={off || !st.proc_enable}
                  onChange={(v) => change('proc_level', parseInt(v, 10), () => FlexSetProcessorLevel(parseInt(v, 10)))} />
                <span className="flex-1" />
              </div>
              <LevelRow label="VOX" on={st.vox_enable} disabled={off} value={st.vox_level} sliderAccent="#16a34a"
                onToggle={() => change('vox_enable', !st.vox_enable, () => FlexSetVox(!st.vox_enable))}
                onLevel={(v) => change('vox_level', v, () => FlexSetVoxLevel(v))} />
              <div className="flex items-center gap-2">
                <span className="w-14 shrink-0 text-[11px] font-bold text-muted-foreground pl-0.5">VOX Dly</span>
                <Slider value={st.vox_delay} disabled={off || !st.vox_enable} accent="#16a34a"
                  onChange={(v) => change('vox_delay', v, () => FlexSetVoxDelay(v))} />
                <span className="w-8 text-right text-xs font-mono tabular-nums text-muted-foreground">{st.vox_delay}</span>
              </div>
              <LevelRow label="MON" on={st.mon} disabled={off} value={st.mon_level} accent="cyan" sliderAccent="#0891b2"
                onToggle={() => change('mon', !st.mon, () => FlexSetMon(!st.mon))}
                onLevel={(v) => change('mon_level', v, () => FlexSetMonLevel(v))} />
              <div className="flex items-center gap-2">
                <span className="w-14 shrink-0 text-[11px] font-bold text-muted-foreground">MIC</span>
                <Slider value={st.mic_level} disabled={off} accent="#2563eb" onChange={(v) => change('mic_level', v, () => FlexSetMic(v))} />
                <span className="w-8 text-right text-xs font-mono tabular-nums text-muted-foreground">{st.mic_level}</span>
              </div>
            </div>
            ) : (
            /* CW keyer controls (replace VOX/PROC/MIC when the slice is in CW). */
            <div className="border-t border-border/60 pt-3 space-y-3">
              <div className="flex items-center gap-2">
                <span className="w-16 shrink-0 text-[11px] font-bold text-muted-foreground">Speed</span>
                <Slider value={st.cw_speed} disabled={off} max={60} accent="#0d9488" onChange={(v) => { change('cw_speed', v, () => FlexSetCWSpeed(v)); onCWSpeed?.(v); }} />
                <span className="w-12 text-right text-xs font-mono tabular-nums text-muted-foreground">{st.cw_speed} wpm</span>
              </div>
              <div className="flex items-center gap-2">
                <span className="w-16 shrink-0 text-[11px] font-bold text-muted-foreground">Pitch</span>
                <Slider value={st.cw_pitch} disabled={off} max={1000} step={10} accent="#7c3aed" onChange={(v) => change('cw_pitch', v, () => FlexSetCWPitch(v))} />
                <span className="w-12 text-right text-xs font-mono tabular-nums text-muted-foreground">{st.cw_pitch} Hz</span>
              </div>
              <div className="flex items-center gap-2">
                <span className="w-16 shrink-0 text-[11px] font-bold text-muted-foreground">Delay</span>
                <Slider value={st.cw_break_in_delay} disabled={off} max={1000} step={1} accent="#d97706" onChange={(v) => change('cw_break_in_delay', v, () => FlexSetCWBreakInDelay(v))} />
                <span className="w-12 text-right text-xs font-mono tabular-nums text-muted-foreground">{st.cw_break_in_delay} ms</span>
              </div>
              <LevelRow label="STONE" on={st.cw_sidetone} disabled={off} value={st.cw_mon_level} accent="cyan" sliderAccent="#0891b2"
                onToggle={() => change('cw_sidetone', !st.cw_sidetone, () => FlexSetCWSidetone(!st.cw_sidetone))}
                onLevel={(v) => change('cw_mon_level', v, () => FlexSetSidetoneLevel(v))} />
            </div>
            )}
          </Card>

          {/* RECEIVE */}
          <Card icon={AudioLines} title="Receive (active slice)" accent="#0891b2">
            {((st.ant_list?.length ?? 0) > 0 || (st.tx_ant_list?.length ?? 0) > 0) && (
              <div className="flex items-center gap-2">
                <span className="w-14 shrink-0 text-[11px] font-bold text-muted-foreground">Ant</span>
                <div className="flex items-center gap-1.5 flex-1 min-w-0">
                  <span className="text-[10px] text-muted-foreground">RX</span>
                  <select disabled={rxOff} value={st.rx_ant ?? ''}
                    onChange={(e) => change('rx_ant', e.target.value, () => FlexSetRXAntenna(e.target.value))}
                    className="h-7 flex-1 min-w-0 rounded-md border border-input bg-background px-1.5 text-xs font-mono disabled:opacity-40">
                    {(st.ant_list ?? []).map((a) => <option key={a} value={a}>{a}</option>)}
                  </select>
                  <span className="text-[10px] text-muted-foreground">TX</span>
                  <select disabled={rxOff} value={st.tx_ant ?? ''}
                    onChange={(e) => change('tx_ant', e.target.value, () => FlexSetTXAntenna(e.target.value))}
                    className="h-7 flex-1 min-w-0 rounded-md border border-input bg-background px-1.5 text-xs font-mono disabled:opacity-40">
                    {((st.tx_ant_list?.length ? st.tx_ant_list : st.ant_list) ?? []).map((a) => <option key={a} value={a}>{a}</option>)}
                  </select>
                </div>
              </div>
            )}
            <div className="flex items-center gap-2">
              <span className="w-14 shrink-0 text-[11px] font-bold text-muted-foreground">AGC</span>
              <Segmented value={(st.agc_mode || 'med').toLowerCase()} options={AGC} disabled={rxOff}
                onChange={(v) => change('agc_mode', v, () => FlexSetAGCMode(v))} />
            </div>
            <div className="flex items-center gap-2">
              <span className="w-14 shrink-0 text-[11px] font-bold text-muted-foreground">AF</span>
              <button type="button" disabled={rxOff}
                title={st.mute ? 'Muted — click to unmute' : 'Mute RX audio'}
                onClick={() => change('mute', !st.mute, () => FlexSetMute(!st.mute))}
                className={cn('shrink-0 rounded p-1 transition-colors disabled:opacity-30',
                  st.mute ? 'bg-red-600 text-white' : 'text-muted-foreground hover:bg-muted')}>
                {st.mute ? <VolumeX className="size-3.5" /> : <Volume2 className="size-3.5" />}
              </button>
              <Slider value={st.audio_level} disabled={rxOff || st.mute} accent="#16a34a" onChange={(v) => change('audio_level', v, () => FlexSetAudioLevel(v))} />
              <span className="w-8 text-right text-xs font-mono tabular-nums text-muted-foreground">{st.mute ? '—' : st.audio_level}</span>
            </div>
            <div className="flex items-center gap-2">
              <span className="w-14 shrink-0 text-[11px] font-bold text-muted-foreground">AGC-T</span>
              <Slider value={st.agc_threshold} disabled={rxOff} accent="#64748b" onChange={(v) => change('agc_threshold', v, () => FlexSetAGCThreshold(v))} />
              <span className="w-8 text-right text-xs font-mono tabular-nums text-muted-foreground">{st.agc_threshold}</span>
            </div>
            <div className="border-t border-border/60 pt-3 space-y-3">
              <LevelRow label="NB" on={st.nb} disabled={rxOff} value={st.nb_level} accent="amber" sliderAccent="#d97706"
                onToggle={() => change('nb', !st.nb, () => FlexSetNB(!st.nb))}
                onLevel={(v) => change('nb_level', v, () => FlexSetNBLevel(v))} />
              <LevelRow label="NR" on={st.nr} disabled={rxOff} value={st.nr_level} accent="cyan" sliderAccent="#0891b2"
                onToggle={() => change('nr', !st.nr, () => FlexSetNR(!st.nr))}
                onLevel={(v) => change('nr_level', v, () => FlexSetNRLevel(v))} />
              {/* ANF (auto notch) is for carriers in voice — meaningless on a CW tone, so hide it in CW. */}
              {!isCW && (
                <LevelRow label="ANF" on={st.anf} disabled={rxOff} value={st.anf_level} accent="violet" sliderAccent="#7c3aed"
                  onToggle={() => change('anf', !st.anf, () => FlexSetANF(!st.anf))}
                  onLevel={(v) => change('anf_level', v, () => FlexSetANFLevel(v))} />
              )}
            </div>
            {isCW && (
              <div className="border-t border-border/60 pt-3 space-y-3">
                <LevelRow label="APF" on={st.apf} disabled={rxOff} value={st.apf_level} accent="emerald" sliderAccent="#16a34a"
                  onToggle={() => change('apf', !st.apf, () => FlexSetAPF(!st.apf))}
                  onLevel={(v) => change('apf_level', v, () => FlexSetAPFLevel(v))} />
                <div className="flex items-center gap-2">
                  <span className="w-14 shrink-0 text-[11px] font-bold text-muted-foreground">Filter</span>
                  <div className="inline-flex rounded-md border border-border overflow-hidden">
                    {CW_BW.map((bw) => (
                      <button key={bw} type="button" disabled={rxOff}
                        onClick={() => { setSt((p) => { const c = ((p.filter_lo || 0) + (p.filter_hi || 0)) ? Math.round(((p.filter_lo || 0) + (p.filter_hi || 0)) / 2) : (p.cw_pitch || 600); return { ...p, filter_lo: c - bw / 2, filter_hi: c + bw / 2 }; }); FlexSetCWFilter(bw).catch(() => {}); }}
                        className={cn('px-2 py-1 text-[11px] font-bold tracking-wide transition-colors disabled:opacity-30 border-l border-border first:border-l-0',
                          Math.abs(curBW - bw) <= 1 ? 'bg-primary text-primary-foreground' : 'bg-card text-muted-foreground hover:bg-muted')}>
                        {bw}
                      </button>
                    ))}
                  </div>
                  <span className="text-[10px] text-muted-foreground/70 font-mono">Hz</span>
                </div>
              </div>
            )}
            {!isCW && (
              <div className="border-t border-border/60 pt-3">
                <div className="flex items-center gap-2">
                  <span className="w-14 shrink-0 text-[11px] font-bold text-muted-foreground">Filter</span>
                  <div className="inline-flex rounded-md border border-border overflow-hidden">
                    {SSB_BW.map((bw) => (
                      <button key={bw} type="button" disabled={rxOff}
                        onClick={() => {
                          const lsb = (st.mode || '').toUpperCase().includes('LSB');
                          let lo: number, hi: number;
                          if (lsb) { const near = (st.filter_hi && st.filter_hi < 0) ? st.filter_hi : -100; hi = near; lo = near - bw; }
                          else { const near = (st.filter_lo && st.filter_lo > 0) ? st.filter_lo : 100; lo = near; hi = near + bw; }
                          setSt((p) => ({ ...p, filter_lo: lo, filter_hi: hi }));
                          FlexSetFilter(lo, hi).catch(() => {});
                        }}
                        className={cn('px-1.5 py-1 text-[11px] font-bold tracking-wide transition-colors disabled:opacity-30 border-l border-border first:border-l-0',
                          bw === ssbActiveBW ? 'bg-primary text-primary-foreground' : 'bg-card text-muted-foreground hover:bg-muted')}>
                        {(bw / 1000).toFixed(1)}
                      </button>
                    ))}
                  </div>
                  <span className="text-[10px] text-muted-foreground/70 font-mono">kHz</span>
                </div>
              </div>
            )}
          </Card>
        </div>

        {/* External amplifier (PowerGenius XL) — only when detected. */}
        {st.amp_available && (
          <Card icon={Flame} title={`Amplifier${st.amp_model ? ' · ' + st.amp_model : ''}`} accent="#ea580c">
            <div className="flex items-center gap-3">
              <button type="button" disabled={off}
                onClick={() => change('amp_operate', !st.amp_operate, () => FlexAmpOperate(!st.amp_operate))}
                className={cn('px-4 py-2 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
                  st.amp_operate ? 'bg-orange-600 text-white border-orange-600 shadow-[0_0_14px] shadow-orange-600/50' : 'bg-card text-orange-700 border-orange-400 hover:bg-orange-50')}>
                {st.amp_operate ? 'OPERATE' : 'STANDBY'}
              </button>
              <span className="text-xs text-muted-foreground">
                {st.amp_operate ? 'Amplifier is in line (transmitting through PA).' : 'Amplifier bypassed (standby).'}
              </span>
              {/* Fan mode — shown when the PowerGenius is configured (Settings →
                  PowerGenius). The dot shows the direct-connection state; the
                  selector is disabled until connected (hover it for the error). */}
              {(pg.host || pg.connected) && (
                <label className="flex items-center gap-1.5 text-xs" title={pg.connected ? 'PowerGenius connected' : (pg.last_error || 'PowerGenius offline')}>
                  <span className={cn('size-1.5 rounded-full', pg.connected ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.8)]' : 'bg-rose-500')} />
                  <span className="text-muted-foreground">Fan</span>
                  <select
                    disabled={!pg.connected}
                    value={(pg.fan_mode || 'CONTEST').toUpperCase()}
                    onChange={(e) => { const v = e.target.value; setPg((s) => ({ ...s, fan_mode: v })); PGXLSetFanMode(v).catch(() => {}); }}
                    className="h-8 rounded-md border border-orange-300 bg-card px-2 text-xs font-semibold text-orange-800 outline-none focus:border-orange-500 disabled:opacity-40"
                  >
                    <option value="STANDARD">Standard</option>
                    <option value="CONTEST">Contest</option>
                    <option value="BROADCAST">Broadcast</option>
                  </select>
                </label>
              )}
              <div className="flex-1" />
              {st.amp_fault && st.amp_fault !== 'NONE' && (
                <span className="px-2 py-1 rounded bg-rose-100 text-rose-800 text-xs font-bold">FAULT: {st.amp_fault}</span>
              )}
            </div>
          </Card>
        )}

        {/* Live meters (UDP VITA-49 stream) */}
        <Card icon={Gauge} title="Meters">
          {(() => {
            const meters = st.meters || [];
            if (off || meters.length === 0) {
              return <p className="text-[11px] text-muted-foreground text-center py-2">No meters yet — waiting for the radio's UDP stream…</p>;
            }
            const isDbm = (m?: Meter) => !!m && /dbm/i.test(m.unit || '');
            const dbmToW = (d: number) => Math.pow(10, (d - 30) / 10);
            // Radio meters (exclude the amplifier's, which we show separately).
            const radio = (name: string) => meters.find((m) =>
              (m.name || '').toUpperCase().includes(name) && !(m.src || '').toUpperCase().includes('AMP'));
            const sig = radio('LEVEL') || radio('SIGNAL');
            const fwd = radio('FWDPWR');
            const swr = radio('SWR');
            const amp = meters.filter((m) => (m.src || '').toUpperCase().includes('AMP')
              && !/^(RL|DRV)$/i.test((m.name || '').trim()));
            const accentFor = (m: Meter) => /swr/i.test(`${m.unit}${m.name}`) ? '#dc2626'
              : /temp|degc|degf/i.test(`${m.unit}${m.name}`) ? '#ea580c'
              : /volt/i.test(m.unit || '') ? '#2563eb' : '#16a34a';
            // S-meter: dBm → S-units (S9 = -73 dBm on HF, 6 dB per unit).
            const sUnit = (dbm: number) => {
              const s = (dbm + 127) / 6; // S0 = -127 dBm
              if (s >= 9) {
                const over = Math.round(dbm + 73); // dB over S9
                return { display: over > 0 ? `S9+${over}` : 'S9', bar: s };
              }
              return { display: `S${Math.max(0, Math.round(s))}`, bar: Math.max(0, s) };
            };
            const cur = [
              sig && (() => { const dbm = peakHold('s', sig.value); const s = sUnit(dbm); return (
                <MeterBar key="s" label="S-METER" value={s.bar} lo={0} hi={19} accent="#16a34a" display={s.display} extra={`${dbm.toFixed(1)} dBm`}
                  segColor={(fr) => { const sv = fr * 19; return sv < 9 ? '#16a34a' : sv < 12.33 ? '#f59e0b' : '#dc2626'; }} />
              ); })(),
              fwd && (() => { const w = peakHold('p', isDbm(fwd) ? dbmToW(fwd.value) : fwd.value); return (
                <MeterBar key="p" label="PWR" unit="W" lo={0} hi={120} accent="#dc2626"
                  value={w} extra={isDbm(fwd) ? `${fwd.value.toFixed(1)} dBm` : undefined} />
              ); })(),
              swr && <MeterBar key="w" label="SWR" value={peakHold('w', swr.value)} unit="" lo={1} hi={3} accent="#d97706" />,
            ].filter(Boolean);
            return (
              <>
                <div className="grid grid-cols-2 sm:grid-cols-3 gap-2">{cur}</div>
                {amp.length > 0 && (
                  <div className="border-t border-border/60 pt-2 mt-1">
                    <div className="text-[10px] font-bold tracking-wider text-muted-foreground mb-1.5">AMPLIFIER</div>
                    <div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
                      {amp.map((m) => {
                        if (/fwd|pwr/i.test(m.name || '') && isDbm(m)) {
                          return <MeterBar key={m.id} label={m.name || `AMP ${m.id}`} value={peakHold(`amp${m.id}`, dbmToW(m.value))} unit="W" lo={0} hi={2000} accent="#dc2626" extra={`${m.value.toFixed(1)} dBm`} />;
                        }
                        return <MeterBar key={m.id} label={m.name || `AMP ${m.id}`} value={peakHold(`amp${m.id}`, m.value)} unit={m.unit} lo={m.lo} hi={m.hi} accent={accentFor(m)} />;
                      })}
                    </div>
                  </div>
                )}
              </>
            );
          })()}
        </Card>
      </div>
    </div>
  );
}
