import { useRef } from 'react';
import { Flame, X, Power } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useI18n } from '@/lib/i18n';
import { MeterBar } from '@/components/MeterBar';
import { AmpOperate, AmpPower, AmpPowerLevel, AmpFanMode, FlexAmpOperate } from '../../wailsjs/go/main/App';

// AmpWidget — the docked amplifier widget, the compact counterpart of AmpCard
// (which is the full-size card shown in the FlexRadio panel and Station
// Control). It covers all three families, since an operator running an SPE and
// a PowerGenius wants one widget, not two:
//   • SPE Expert  — OPERATE, power ON/OFF, L/M/H, live power / SWR / temp
//   • ACOM        — OPERATE, power ON/OFF, live power / SWR / temp
//   • PowerGenius — OPERATE and meters ride on the FlexRadio, fan mode on GSCP
// With several amplifiers configured the caller passes a selection ("all" or an
// amp id) chosen from the toolbar icon's dropdown.

type Amp = { id: string; name: string; type?: string; spe?: any; acom?: any; kpa?: any; pgxl?: any };

// Full-scale watts for the output bar, from the model string.
function maxW(model?: string, fallback = 1300): number {
  const m = (model || '').toUpperCase();
  if (m.includes('20K') || m.includes('2K')) return 2000;
  if (m.includes('15K')) return 1500;
  if (m.includes('13K')) return 1300;
  return fallback;
}

function swrColour(swr?: number): string {
  if (!swr || swr <= 0) return 'text-muted-foreground';
  if (swr <= 1.5) return 'text-success';
  if (swr <= 2.0) return 'text-warning';
  return 'text-danger';
}

// PowerMeter — the output-power LED bar that closes each column, straight under
// the controls (no filler above it: pushed to the bottom it left a band of dead
// space in the middle of the column). Uses the app-wide MeterBar, compact size,
// so it reads exactly like the meters on the rig panels.
function PowerMeter({ label, watts, maxWatts }: { label: string; watts: number; maxWatts: number }) {
  return (
    <MeterBar compact label={label} value={watts} unit="W" lo={0} hi={maxWatts}
      display={`${Math.round(watts)} W`}
      segColor={(f) => (f > 0.9 ? '#dc2626' : f > 0.75 ? '#f59e0b' : '#ea580c')} />
  );
}

// usePeakHold keeps the highest reading seen for a short window, so a reading
// sampled between two syllables doesn't collapse the meter. `live` is the key
// part: pass false the moment the carrier drops and the held value is thrown
// away at once — holding it there made releasing the PTT look like a five
// second lag, which read as the whole app being slow.
function usePeakHold(windowMs = 1000) {
  const peak = useRef<Record<string, { v: number; t: number }>>({});
  return (key: string, val: number, live = true) => {
    if (!live) { delete peak.current[key]; return val; }
    const now = Date.now();
    const p = peak.current[key];
    if (!p || val >= p.v || now - p.t > windowMs) { peak.current[key] = { v: val, t: now }; return val; }
    return p.v;
  };
}

// Stat — one small labelled figure (power, SWR, temperature).
function Stat({ label, value, className }: { label: string; value: string; className?: string }) {
  return (
    <div className="rounded-lg border border-border bg-card/70 px-1.5 py-1 text-center">
      <div className="text-[9px] uppercase tracking-wider text-muted-foreground leading-tight">{label}</div>
      <div className={cn('text-sm font-bold font-mono leading-tight', className ?? 'text-foreground/80')}>{value}</div>
    </div>
  );
}

// OperateButton — the STANDBY/OPERATE toggle, identical in behaviour across the
// three families (only where the state comes from differs).
function OperateButton({ operate, disabled, onClick, t }: {
  operate: boolean; disabled: boolean; onClick: () => void; t: (k: string, v?: any) => string;
}) {
  return (
    <button type="button" disabled={disabled} onClick={onClick}
      className={cn('w-full inline-flex items-center justify-center gap-1.5 rounded-lg text-xs font-bold py-1.5 border transition-all active:scale-[0.98] disabled:opacity-40',
        operate
          ? 'bg-gradient-to-b from-amber-400 to-amber-600 text-white border-amber-300/60 shadow-[0_0_9px_rgba(245,158,11,0.45)]'
          : 'bg-card text-muted-foreground border-border hover:bg-muted hover:text-foreground')}>
      <Power className="size-3.5" />
      {operate ? t('ampw.operate') : t('ampw.standby')}
    </button>
  );
}

// AmpBlock — one amplifier inside the widget.
function AmpBlock({ amp, flex, showName, t }: {
  amp: Amp; flex: any; showName: boolean; t: (k: string, v?: any) => string;
}) {
  const spe = amp.spe, acom = amp.acom, kpaAmp = amp.kpa;
  const hold = usePeakHold();

  // One block for the three amplifiers OpsLog drives over its own link. They
  // report the same handful of things under different names, so the differences
  // are named here rather than spread through the markup.
  if (spe || acom || kpaAmp) {
    const s = spe || acom || kpaAmp;
    // The amp reports zero watts on receive, so its TX flag clears the meter as
    // soon as the operator lets go — and the radio's own flag, when we have one,
    // gets there first (the amp is polled on its own slower cycle).
    const txing = typeof flex?.transmitting === 'boolean' ? flex.transmitting : s.tx !== false;
    const w = hold('w', Number(spe ? s.output_w : s.fwd_w) || 0, txing);
    const swr = Number(spe ? s.swr_ant : s.swr) || 0;
    // The full-scale mark. A KPA500 reading against a 1500 W scale would look
    // idle at full output, so the model decides it.
    const hi = spe ? maxW(s.model)
      : kpaAmp ? (String(s.model).includes('500') ? 500 : 1500)
      : (Number(s.max_w) || 800);
    // Power ON drives the remote-on control lines, so it stays available while
    // the amplifier is off and reporting nothing — but only over a serial link.
    // A KPA answers ^ON while its main supplies are off — the sleeping
    // microcontroller stays awake for exactly that — so power-on is available
    // whenever the link itself is up, over serial and over the network alike.
    const canPowerOn = spe ? (s.connected || s.transport === 'serial')
      : kpaAmp ? !!s.connected
      : (s.port_open && s.transport === 'serial');
    return (
      <div className="h-full flex flex-col gap-1.5">
        {showName && (
          <div className="flex items-center gap-1.5">
            <span className={cn('size-1.5 rounded-full shrink-0', s.connected ? 'bg-success shadow-[0_0_6px_rgba(16,185,129,0.8)]' : 'bg-danger')} />
            <span className="text-[10px] font-bold uppercase tracking-wider truncate">{amp.name || (spe ? 'SPE' : kpaAmp ? (s.model || 'KPA') : 'Acom')}</span>
            {s.connected && s.band && <span className="ml-auto text-[9px] text-muted-foreground shrink-0">{s.band}</span>}
          </div>
        )}
        <OperateButton operate={!!s.operate} disabled={!s.connected} t={t}
          onClick={() => AmpOperate(amp.id, !s.operate).catch(() => {})} />
        <div className="flex gap-1.5">
          <button type="button" disabled={!canPowerOn}
            onClick={() => AmpPower(amp.id, true).catch(() => {})}
            className="flex-1 rounded-lg border border-success/60 bg-card py-1 text-[11px] font-bold text-success hover:bg-success/15 disabled:opacity-30">
            {t('ampw.on')}
          </button>
          <button type="button" disabled={!s.connected}
            onClick={() => AmpPower(amp.id, false).catch(() => {})}
            className="flex-1 rounded-lg border border-danger/60 bg-card py-1 text-[11px] font-bold text-danger hover:bg-danger/15 disabled:opacity-30">
            {t('ampw.off')}
          </button>
        </div>
        {/* Output level — SPE only; an ACOM has no L/M/H over CAT. */}
        {spe && (
          <div className="grid grid-cols-3 gap-1">
            {(['L', 'M', 'H'] as const).map((lvl) => {
              const active = (s.power_level || '').trim().toUpperCase() === lvl;
              return (
                <button key={lvl} type="button" disabled={!s.connected}
                  onClick={() => AmpPowerLevel(amp.id, lvl).catch(() => {})}
                  className={cn('rounded-md border py-0.5 text-[10px] font-bold transition-colors disabled:opacity-30',
                    active ? 'bg-primary text-primary-foreground border-primary' : 'bg-card text-muted-foreground border-border hover:bg-muted')}>
                  {t(`ampw.lvl${lvl}`)}
                </button>
              );
            })}
          </div>
        )}
        {s.connected ? (
          <div className="grid grid-cols-3 gap-1">
            <Stat label={t('ampw.pwr')} value={`${Math.round(w)}`} />
            <Stat label={t('ampw.swr')} value={swr > 0 ? swr.toFixed(1) : '—'} className={swrColour(swr)} />
            <Stat label={t('ampw.temp')} value={`${Number(s.temp_c) || 0}°`} />
          </div>
        ) : (
          <div className="text-[10px] text-center text-muted-foreground italic py-1">{t('ampw.offline')}</div>
        )}
        {(s.warnings || s.alarms || s.err_text || s.fault_text) && (
          <div className="rounded-md border border-danger-border bg-danger-muted text-danger-muted-foreground text-[9px] px-1.5 py-0.5 text-center break-words">
            {s.fault_text || s.err_text || `${s.warnings || ''} ${s.alarms || ''}`.trim()}
          </div>
        )}
        {/* Said where the fault is read, because it is the way out of it: on a
            KPA, going to OPERATE clears the current fault — everything except
            temperature, which clears by cooling. */}
        {kpaAmp && s.fault_text && (
          <div className="text-[9px] text-center text-muted-foreground">{t('ampw.kpaClear')}</div>
        )}
        <PowerMeter label={t('flxp.outputPower')} watts={s.connected ? w : 0} maxWatts={hi} />
      </div>
    );
  }

  // PowerGenius XL: OPERATE and the meters come from the radio when the Flex
  // reports the amp; the fan mode rides on our own GSCP link.
  const pg = amp.pgxl || {};
  const viaFlex = !!flex?.amp_available;
  const operate = viaFlex ? !!flex?.amp_operate : !!pg.operate;
  const connected = !!pg.connected || viaFlex;
  const fault = flex?.amp_fault;
  const meters = ((flex?.meters as any[]) || []).filter((m) => (m.src || '').toUpperCase().includes('AMP'));
  const meter = (re: RegExp) => meters.find((m) => re.test((m.name || '').trim()));
  const dbmToW = (d: number) => Math.pow(10, (d - 30) / 10);
  const fwd = meter(/^fwd|pwr/i);
  const flexW = fwd ? (/dbm/i.test(fwd.unit || '') ? dbmToW(fwd.value) : fwd.value) : 0;
  // Whether there is power is the RADIO's transmit flag: it flips the instant
  // the PTT does, where the amplifier's own status poll answers a second and a
  // half late and holds TRANSMIT past the carrier.
  //
  // How MUCH is the live reading — the radio's meter stream, or the amp's own
  // forward figure when there is no Flex. NOT the amp's "peakfwd": that is a
  // latched maximum which is never reset AND survives in the last-known status
  // after the amp disconnects, so it reported 1350 W from an old transmission
  // while the radio was putting out 10 W into a disconnected amplifier.
  const txing = typeof flex?.transmitting === 'boolean'
    ? flex.transmitting
    : fwd ? flexW >= 1 : /TRANSMIT/i.test(pg.state || '');
  // Prefer the radio's VITA meter stream when it is actually flowing (local or
  // over a VPN) — it is fast and matches SmartSDR. Fall back to the amp's OWN
  // link only when the radio isn't streaming its meters (a remote link without
  // the VITA path), so the reading survives but is naturally slower.
  const liveW = flexW > 0 ? flexW : (pg.connected ? Number(pg.fwd_w) || 0 : 0);
  const fwdW = hold('fwd', txing ? liveW : 0, txing);
  const id = meter(/^ID$|current/i);
  // Live drain current, same source preference. peak_id latches like peak_w, so
  // use the instantaneous 'id' and gate it on the radio's transmit flag.
  const idA = id ? Number(id.value) : (txing ? Number(pg.id) || undefined : 0);
  const temp = meter(/temp/i);
  // VITA temp matches SmartSDR (the amp's direct link exposes a different sensor
  // that can read ~10°C higher); fall back to the direct temp when no VITA.
  const tempC = temp ? temp.value : (pg.temperature > 0 ? pg.temperature : undefined);

  return (
    <div className="h-full flex flex-col gap-1.5">
      {showName && (
        <div className="flex items-center gap-1.5">
          <span className={cn('size-1.5 rounded-full shrink-0', connected ? 'bg-success shadow-[0_0_6px_rgba(16,185,129,0.8)]' : 'bg-danger')} />
          <span className="text-[10px] font-bold uppercase tracking-wider truncate">{amp.name || 'PGXL'}</span>
        </div>
      )}
      <OperateButton operate={operate} disabled={!connected} t={t}
        onClick={() => (viaFlex ? FlexAmpOperate(!operate) : AmpOperate(amp.id, !operate)).catch(() => {})} />
      {connected ? (
        <div className="grid grid-cols-3 gap-1">
          <Stat label={t('ampw.pwr')} value={`${Math.round(fwdW)}`} />
          <Stat label={t('ampw.id')} value={idA != null ? idA.toFixed(1) : '—'} />
          <Stat label={t('ampw.temp')} value={tempC != null ? `${Math.round(tempC)}°` : '—'} />
        </div>
      ) : (
        <div className="text-[10px] text-center text-muted-foreground italic py-1">{t('ampw.offline')}</div>
      )}
      {(pg.host || pg.connected) && (
        <select
          disabled={!pg.connected}
          value={(pg.fan_mode || 'CONTEST').toUpperCase()}
          onChange={(e) => AmpFanMode(amp.id, e.target.value).catch(() => {})}
          title={t('ampw.fan')}
          className="w-full h-7 rounded-md border border-border bg-card px-1.5 text-[10px] font-semibold outline-none focus:border-warning disabled:opacity-40">
          <option value="STANDARD">{t('flxp.fanStandard')}</option>
          <option value="CONTEST">{t('flxp.fanContest')}</option>
          <option value="BROADCAST">{t('flxp.fanBroadcast')}</option>
        </select>
      )}
      {fault && fault !== 'NONE' && (
        <div className="rounded-md border border-danger-border bg-danger-muted text-danger-muted-foreground text-[9px] px-1.5 py-0.5 text-center break-words">
          {t('flxp.fault')}: {fault}
        </div>
      )}
      <PowerMeter label={t('flxp.outputPower')} watts={connected ? fwdW : 0} maxWatts={2000} />
    </div>
  );
}

export function AmpWidget({ amps, flex, sel, onClose }: {
  amps: Amp[];
  flex: any;
  sel: string; // "all" or an amp id
  onClose: () => void;
}) {
  const { t } = useI18n();
  // An id that no longer matches (the amp was removed in Settings) falls back to
  // showing everything rather than an empty widget.
  const shown = sel === 'all' ? amps : (amps.filter((a) => a.id === sel).length ? amps.filter((a) => a.id === sel) : amps);
  const anyOnline = shown.some((a) => a.spe?.connected || a.acom?.connected || a.pgxl?.connected || flex?.amp_available);
  const title = shown.length === 1 ? (shown[0].name || t('ampw.title')) : t('ampw.title');

  return (
    <div className="h-full flex flex-col rounded-xl border border-border bg-gradient-to-b from-card to-muted/30 shadow-sm overflow-hidden">
      <div className="flex items-center gap-2 px-3 py-2 border-b border-border/60 bg-muted/40 shrink-0">
        <Flame className={cn('size-4', anyOnline ? 'text-warning drop-shadow-[0_0_3px_rgba(245,158,11,0.55)]' : 'text-muted-foreground')} />
        <span className="text-xs font-bold uppercase tracking-[0.18em] text-foreground/80 truncate">{title}</span>
        <span className="flex-1" />
        <button type="button" onClick={onClose} className="ml-1 text-muted-foreground hover:text-foreground transition-colors" title={t('ampw.close')}>
          <X className="size-3.5" />
        </button>
      </div>
      {/* Several amplifiers sit SIDE BY SIDE, one column each: stacked, two amps
          made a widget taller than the dock row and both ended up half hidden. */}
      <div className="flex-1 min-h-0 overflow-y-auto p-2.5">
        {shown.length === 0 ? (
          <div className="text-[11px] text-center text-muted-foreground italic py-2">{t('ampw.none')}</div>
        ) : (
          <div className="grid gap-2.5 h-full" style={{ gridTemplateColumns: `repeat(${shown.length}, minmax(0, 1fr))` }}>
            {shown.map((a, i) => (
              <div key={a.id} className={cn(i > 0 && 'pl-2.5 border-l border-border/50')}>
                <AmpBlock amp={a} flex={flex} showName={shown.length > 1} t={t} />
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}
