import { useEffect, useRef, useState } from 'react';
import { Radio, Power, Activity, AudioLines, SlidersHorizontal } from 'lucide-react';
import {
  GetKenwoodState, RefreshKenwood, SetKenwoodPower, SetKenwoodAFGain, SetKenwoodTX, TuneKenwoodATU,
  SetKenwoodRFGain, SetKenwoodMicGain, SetKenwoodSquelch, SetKenwoodPreamp, SetKenwoodAtt,
  SetKenwoodNB, SetKenwoodNR, SetKenwoodAGC, SetKenwoodFilter, SetKenwoodAntenna,
  SetKenwoodRIT, SetKenwoodXIT, ClearKenwoodRIT, SetKenwoodKeySpeed, ToggleKenwoodATU, SetKenwoodRITOffset, SetKenwoodPanelMode,
  GetCATState,
} from '../../wailsjs/go/main/App';
import { cn } from '@/lib/utils';
import { useI18n } from '@/lib/i18n';
import { sMeterRST } from '@/lib/rst';
import { ShiftRow } from '@/components/ShiftRow';
import { MeterBar } from '@/components/MeterBar';
import { WheelRange } from '@/components/WheelRange';

type KenwoodState = {
  available: boolean; model?: string; elecraft: boolean; mode?: string; data_sub?: string;
  transmitting: boolean; split: boolean; split_tx_hz?: number;
  s_meter: number; s_meter_raw: number;
  power_meter: number; power_w?: number; swr: number; swr_raw: number;
  rf_power: number; af_gain: number; rf_gain: number; mic_gain: number; squelch: number;
  preamp: boolean; att: boolean; nb: boolean; nr: boolean; agc?: string;
  filter_hz: number; antenna: number; rit: boolean; xit: boolean; rit_offset: number; key_speed: number;
  meters_provisional: boolean;
};

const ZERO: KenwoodState = {
  available: false, elecraft: false, transmitting: false, split: false,
  s_meter: 0, s_meter_raw: 0, power_meter: 0, swr: 0, swr_raw: 0,
  rf_power: 0, af_gain: 0, rf_gain: 0, mic_gain: 0, squelch: 0,
  preamp: false, att: false, nb: false, nr: false,
  filter_hz: 0, antenna: 0, rit: false, xit: false, rit_offset: 0, key_speed: 0,
  meters_provisional: true,
};

// Filter widths worth a button. CW work happens at 200-500 Hz, SSB at 2.4-2.8
// kHz; the rest of the range is reachable from the radio's own knob, and a
// panel offering thirty widths is slower to use than the knob it replaces.
// 4.0k is the K3 maximum and the one FT8 wants: a 2.8 kHz filter clips the
// top of the FT8 sub-band, and the decodes that go missing are the ones
// nobody notices are missing.
const FILTERS = [200, 400, 700, 1000, 1800, 2400, 2800, 4000];

// Raw S-meter → S units, CALIBRATED against a real K3 beside its own display
// (2026-08): raw 5 reads S7 on the radio, raw 9 reads S9+20. So S9 sits near
// raw 6.5 — not 9, which showed everything two S-units low — and each raw step
// above it is worth ~8 dB, shown in the 10 dB steps the K3's own meter uses.
// A Kenwood answers 0-30 on the same command and keeps the simple 1-per-raw
// scale until someone calibrates one against a real radio too.
const K3_S9_RAW = 6.5;
const K3_DB_PER_RAW = 8;
function sParts(rawV: number, elecraft: boolean): { s: number; over: number; label: string } {
  const s9raw = elecraft ? K3_S9_RAW : 9;
  if (rawV >= s9raw) {
    let over = Math.max(0, Math.round((rawV - s9raw) * (elecraft ? K3_DB_PER_RAW : 6)));
    if (elecraft) over = Math.round(over / 10) * 10;
    return { s: 9, over, label: over > 0 ? `S9+${over}` : 'S9' };
  }
  const s = Math.max(0, Math.min(9, Math.round(rawV * (elecraft ? 9 / K3_S9_RAW : 1))));
  return { s, over: 0, label: `S${s}` };
}

// Segment colour, printed the way a radio's own meter is: green up to S9, amber
// through the S9+ range, red once the signal would be reported as 59+20 or more.
function sSegColor(frac: number) {
  if (frac > 0.78) return '#dc2626';
  if (frac > 0.55) return '#f59e0b';
  return '#16a34a';
}

// The card shell the Yaesu and Icom consoles use, so the three read alike.
function Card({ icon: Icon, title, children }: { icon: any; title: 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 text-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>
  );
}

// One button shape for every on/off control, so the panel reads as one
// instrument instead of a collection of differently-styled switches.
function Toggle({ label, on, off, onClick }: { label: string; on: boolean; off: boolean; onClick: () => void }) {
  return (
    <button type="button" disabled={off} onClick={onClick}
      className={cn('px-2 py-1 rounded-md text-[11px] font-bold tracking-wide border transition-all disabled:opacity-30',
        on ? 'bg-primary text-primary-foreground border-primary' : 'bg-card text-muted-foreground border-border hover:border-primary/60')}>
      {label}
    </button>
  );
}

// isDataMode: what the rig reports for MD6 varies — "DATA", or the configured
// digital default (FT8…) — so the DATA family is "not one of the native modes".
function isDataMode(m?: string): boolean {
  switch ((m ?? '').toUpperCase()) {
    case '': case 'CW': case 'USB': case 'LSB': case 'SSB': case 'FM': case 'AM': case 'RTTY':
      return false;
  }
  return true;
}

export function ElecraftPanel({ onReportRST }: { onReportRST?: (rst: string) => void }) {
  const { t } = useI18n();
  const [st, setSt] = useState<KenwoodState>(ZERO);
  // Ctrl+Left/Right shifts the RIT by ±10 Hz while RIT is on — the same
  // keyboard clarifier the Icom and TCI consoles have, for zero-beating a
  // caller without touching the mouse.
  const stRef = useRef(st); stRef.current = st;
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (!e.ctrlKey || (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight')) return;
      const v = stRef.current;
      if (!v.available || !v.rit) return;
      e.preventDefault();
      SetKenwoodRITOffset((v.rit_offset || 0) + (e.key === 'ArrowRight' ? 10 : -10)).catch(() => {});
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);
  const [freqHz, setFreqHz] = useState(0);
  const [err, setErr] = useState('');
  // Optimistic overlay: a slider must follow the finger, not the poll. Dropped
  // once the radio has had time to answer with the value it actually took.
  const [local, setLocal] = useState<{ rf_power?: number; af_gain?: number; rf_gain?: number; mic_gain?: number; squelch?: number; key_speed?: number }>({});
  const localAtRef = useRef(0);

  useEffect(() => {
    let alive = true;
    const tick = async () => {
      try {
        const s = (await GetKenwoodState()) as KenwoodState;
        const c = (await GetCATState()) as any;
        if (!alive) return;
        setSt(s);
        setFreqHz(c?.split && c?.freq_rx_hz > 0 ? c.freq_rx_hz : (c?.freq_hz ?? 0));
        if (Date.now() - localAtRef.current > 1200) setLocal({});
        setErr('');
      } catch (e: any) {
        if (alive) setErr(String(e?.message ?? e));
      }
    };
    tick();
    const id = window.setInterval(tick, 500);
    return () => { alive = false; window.clearInterval(id); };
  }, []);

  const view = { ...st, ...local };
  const off = !st.available;

  const setErrMsg = (e: any) => setErr(String(e?.message ?? e));

  const put = (patch: typeof local, run: () => Promise<any>) => {
    setLocal((p) => ({ ...p, ...patch }));
    localAtRef.current = Date.now();
    run().catch((e) => setErr(String(e?.message ?? e)));
  };

  return (
    <div className="h-full min-h-0 overflow-auto bg-background">
      {/* Same wrapper as the Yaesu, Icom and Flex consoles: capped width and
          CENTRED. A console stretched across a 2000 px window puts a slider a
          hand's width from its own label, and the panel stops reading as one
          instrument. */}
      <div className="max-w-5xl mx-auto p-3 space-y-3">
      {/* VFO + status */}
      <div className="rounded-xl border border-border bg-card shadow-sm px-4 py-3 flex items-center justify-between gap-3 flex-wrap">
        <div>
          <div className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground flex items-center gap-1.5">
            <Radio className="size-3.5" />
            {st.elecraft ? 'Elecraft' : 'Kenwood'} {st.model}
            <span className={cn('size-2 rounded-full', off ? 'bg-muted-foreground/40' : 'bg-success')} />
          </div>
          <div className="text-2xl font-mono tabular-nums font-bold">
            {freqHz > 0 ? (freqHz / 1e6).toFixed(6) : '—'}
          </div>
        </div>
        {/* Mode row. The two DATA buttons are why it exists: MD6 alone keeps
            whatever submode the last session left, and a K3 "in DATA" with FSK D
            still armed keys FT8 with no audio. DATA sends MD6+DT0 (DATA A, the
            soundcard path), RTTY sends MD6+DT2 (FSK D). The active DATA button
            follows the rig's own DT answer. */}
        <div className="inline-flex rounded-md border border-border overflow-hidden">
          {([
            ['CW', 'CW', view.mode === 'CW'],
            ['USB', 'USB', view.mode === 'USB'],
            ['LSB', 'LSB', view.mode === 'LSB'],
            ['DATA', t('k3.modeData'), isDataMode(view.mode) && view.data_sub !== 'FSK D' && view.data_sub !== 'PSK D'],
            ['RTTY', t('k3.modeRtty'), (isDataMode(view.mode) && (view.data_sub === 'FSK D' || view.data_sub === 'PSK D')) || view.mode === 'RTTY'],
          ] as [string, string, boolean][]).map(([cmd, label, on]) => (
            <button key={cmd} type="button" disabled={off}
              title={cmd === 'DATA' ? t('k3.modeDataHint') : cmd === 'RTTY' ? t('k3.modeRttyHint') : label}
              onClick={() => SetKenwoodPanelMode(cmd).catch((e: any) => setErr(String(e?.message ?? e)))}
              className={cn('px-2.5 py-1.5 text-xs font-bold border-l border-border first:border-l-0',
                on ? 'bg-primary text-primary-foreground' : 'bg-card text-muted-foreground hover:bg-muted')}>
              {label}
            </button>
          ))}
        </div>
        <button type="button" className="text-[11px] text-muted-foreground hover:text-foreground flex items-center gap-1"
          onClick={() => RefreshKenwood().catch(() => {})} title={t('k3.refreshHint')}>
          <SlidersHorizontal className="size-3.5" />
        </button>
      </div>

      {off && <div className="text-xs text-muted-foreground px-1">{t('k3.waiting')}</div>}
      {!!err && <div className="text-[11px] text-danger px-1">{err}</div>}

      {/* Meters */}
      <Card icon={Activity} title={t('k3.meters')}>
      <div className="grid grid-cols-1 sm:grid-cols-3 gap-2">
        <MeterBar label="S-METER" value={view.transmitting ? 0 : view.s_meter} lo={0} hi={100}
          accent="#16a34a" segColor={sSegColor}
          display={view.transmitting ? '—' : sParts(view.s_meter_raw, view.elecraft).label}
          onClick={() => {
            if (view.transmitting || !onReportRST) return;
            const sp = sParts(view.s_meter_raw, view.elecraft);
            onReportRST(sMeterRST(sp.s, sp.over, view.mode));
          }}
          title={t('k3.sMeterHint', { raw: String(view.s_meter_raw) })} />
        <MeterBar label="PWR" value={view.transmitting ? view.power_meter : 0} lo={0} hi={100} accent="#0ea5e9"
          display={view.transmitting && view.elecraft ? `${view.power_w ?? 0} W` : undefined} />
        {/* 0 means "not measured", and it must not render as a perfect 1.0:
            a match that looks ideal on an antenna nobody has measured is the one
            reading that can cost a radio. */}
        <MeterBar label="SWR" value={view.transmitting && view.swr > 0 ? view.swr : 1} lo={1} hi={4}
          accent="#f59e0b"
          display={view.transmitting && view.swr > 0 ? view.swr.toFixed(1) : '—'} />
      </div>
      {view.meters_provisional && (
        <p className="text-[10px] text-muted-foreground">{t('k3.provisional')}</p>
      )}
      </Card>

      {/* MOX + TUNE */}
      <div className="flex items-center gap-2">
        <button type="button" disabled={off}
          onClick={() => SetKenwoodTX(!view.transmitting).catch((e) => setErr(String(e?.message ?? e)))}
          className={cn('flex-1 px-3 py-2.5 rounded-lg text-sm font-extrabold tracking-wide border-2 transition-all disabled:opacity-30',
            view.transmitting
              ? 'bg-danger text-danger-foreground border-danger shadow-[0_0_14px] shadow-danger/50'
              : 'bg-card text-danger border-danger hover:bg-danger-muted')}>
          <Power className="size-4 inline mr-1 -mt-0.5" /> MOX
        </button>
        <button type="button" disabled={off}
          onClick={() => TuneKenwoodATU().catch((e) => setErr(String(e?.message ?? e)))}
          title={t('k3.tuneHint')}
          className="flex-1 px-3 py-2.5 rounded-lg text-sm font-extrabold tracking-wide border-2 border-warning text-warning bg-card hover:bg-warning-muted transition-all disabled:opacity-30">
          <Activity className="size-4 inline mr-1 -mt-0.5" /> TUNE
        </button>
        {/* The HOLD of the same switch: tuner in line or bypassed. Two buttons
            because they are two things on the radio — tuning is a cycle you
            start, bypassing is a state you leave it in. */}
        <button type="button" disabled={off}
          onClick={() => ToggleKenwoodATU().catch(setErrMsg)}
          title={t('k3.atuHint')}
          className="px-3 py-2.5 rounded-lg text-sm font-extrabold tracking-wide border-2 border-border text-muted-foreground bg-card hover:bg-muted transition-all disabled:opacity-30">
          ATU
        </button>
      </div>

      {/* Levels — two columns, so a slider stays beside the label it belongs to
          instead of running the width of the window. */}
      <Card icon={SlidersHorizontal} title={t('k3.levels')}>
      <div className="grid grid-cols-1 lg:grid-cols-2 gap-x-6 gap-y-2">
        <label className="flex items-center gap-2 text-xs">
          <span className="w-16 shrink-0 text-muted-foreground">{t('k3.power')}</span>
          <WheelRange min={0} max={110} step={1} disabled={off}
            value={view.rf_power ?? 0}
            onChange={(n) => put({ rf_power: n }, () => SetKenwoodPower(n))} />
          <span className="w-12 text-right font-mono tabular-nums">{view.rf_power ?? 0} W</span>
        </label>
        <label className="flex items-center gap-2 text-xs">
          <span className="w-16 shrink-0 text-muted-foreground flex items-center gap-1">
            <AudioLines className="size-3.5" /> {t('k3.volume')}
          </span>
          <WheelRange min={0} max={100} disabled={off}
            value={view.af_gain ?? 0}
            onChange={(n) => put({ af_gain: n }, () => SetKenwoodAFGain(n))} />
          <span className="w-12 text-right font-mono tabular-nums">{view.af_gain ?? 0}</span>
        </label>
        <label className="flex items-center gap-2 text-xs">
          <span className="w-16 shrink-0 text-muted-foreground">{t('k3.rfGain')}</span>
          <WheelRange min={0} max={100} disabled={off}
            value={view.rf_gain ?? 0}
            onChange={(n) => put({ rf_gain: n }, () => SetKenwoodRFGain(n))} />
          <span className="w-12 text-right font-mono tabular-nums">{view.rf_gain ?? 0}</span>
        </label>
        <label className="flex items-center gap-2 text-xs">
          <span className="w-16 shrink-0 text-muted-foreground">{t('k3.micGain')}</span>
          <WheelRange min={0} max={100} disabled={off}
            value={view.mic_gain ?? 0}
            onChange={(n) => put({ mic_gain: n }, () => SetKenwoodMicGain(n))} />
          <span className="w-12 text-right font-mono tabular-nums">{view.mic_gain ?? 0}</span>
        </label>
        <label className="flex items-center gap-2 text-xs">
          <span className="w-16 shrink-0 text-muted-foreground">{t('k3.squelch')}</span>
          <WheelRange min={0} max={100} disabled={off}
            value={view.squelch ?? 0}
            onChange={(n) => put({ squelch: n }, () => SetKenwoodSquelch(n))} />
          <span className="w-12 text-right font-mono tabular-nums">{view.squelch ?? 0}</span>
        </label>
        {/* CW keyer speed — the radio's own keyer, the one the K3 sends with. */}
        <label className="flex items-center gap-2 text-xs">
          <span className="w-16 shrink-0 text-muted-foreground">{t('k3.keySpeed')}</span>
          <WheelRange min={8} max={50} disabled={off}
            value={view.key_speed || 20}
            onChange={(n) => put({ key_speed: n }, () => SetKenwoodKeySpeed(n))} />
          <span className="w-12 text-right font-mono tabular-nums">{view.key_speed || 0} wpm</span>
        </label>
      </div>
      </Card>

      {/* Receive chain. Toggles, because that is what they are on the radio:
          one press each, and the state comes back from the rig rather than from
          what the button was asked to do. */}
      <Card icon={AudioLines} title={t('k3.receive')}>
      <div className="space-y-2">
        <div className="flex flex-wrap items-center gap-1.5">
          <Toggle label="PRE" on={view.preamp} off={off} onClick={() => SetKenwoodPreamp(!view.preamp).catch(setErrMsg)} />
          <Toggle label="ATT" on={view.att} off={off} onClick={() => SetKenwoodAtt(!view.att).catch(setErrMsg)} />
          <Toggle label="NB" on={view.nb} off={off} onClick={() => SetKenwoodNB(!view.nb).catch(setErrMsg)} />
          <Toggle label="NR" on={view.nr} off={off} onClick={() => SetKenwoodNR(!view.nr).catch(setErrMsg)} />
          <span className="w-2" />
          {['OFF', 'SLOW', 'FAST'].map((a) => (
            <Toggle key={a} label={a} on={(view.agc || '').toUpperCase() === a} off={off}
              onClick={() => SetKenwoodAGC(a).catch(setErrMsg)} />
          ))}
        </div>

        {/* RIT / XIT — the shared ShiftRow, exactly as the Icom and TCI consoles
            drive theirs: ± / wheel / type, Ctrl+←/→ while RIT is on. The K3 has
            ONE offset for both, so both rows show it, like the Icom's. */}
        <div className="space-y-1.5">
          <ShiftRow label="RIT" accent="#8b5cf6" on={view.rit} hz={view.rit_offset || 0} disabled={off}
            onToggle={() => SetKenwoodRIT(!view.rit).catch(setErrMsg)}
            onSet={(hz) => SetKenwoodRITOffset(hz).catch(setErrMsg)} />
          <ShiftRow label="XIT" accent="#f59e0b" on={view.xit} hz={view.rit_offset || 0} disabled={off}
            onToggle={() => SetKenwoodXIT(!view.xit).catch(setErrMsg)}
            onSet={(hz) => SetKenwoodRITOffset(hz).catch(setErrMsg)} />
        </div>
        <div className="flex flex-wrap items-center gap-1.5">
          {/* Antenna only when the radio answered AN — a K3 without the internal
              ATU has one socket and no switch to offer. */}
          {view.antenna > 0 && (<>
            <span className="w-2" />
            <span className="text-[10px] uppercase tracking-wider text-muted-foreground">{t('k3.antenna')}</span>
            {[1, 2].map((n) => (
              <Toggle key={n} label={'ANT' + n} on={view.antenna === n} off={off}
                onClick={() => SetKenwoodAntenna(n).catch(setErrMsg)} />
            ))}
          </>)}
        </div>

        {/* Filter width */}
        <div className="flex flex-wrap items-center gap-1.5">
          <span className="text-[10px] uppercase tracking-wider text-muted-foreground">{t('k3.filter')}</span>
          {FILTERS.map((hz) => (
            <Toggle key={hz} label={hz >= 1000 ? (hz / 1000).toFixed(1) + 'k' : String(hz)}
              on={view.filter_hz === hz} off={off}
              onClick={() => SetKenwoodFilter(hz).catch(setErrMsg)} />
          ))}
          {view.filter_hz > 0 && (
            <span className="text-[10px] font-mono text-muted-foreground">{view.filter_hz} Hz</span>
          )}
        </div>
      </div>
      </Card>
      </div>
    </div>
  );
}
