package cat

import "testing"

// Every known rig's OmniRig behaviour, pinned. These rules genuinely contradict
// each other between models, so a change that fixes one rig must be shown not to
// break another — that is what this table is for.
func TestResolveOmniRigVFOs(t *testing.T) {
	const (
		a14200 = 14200000
		b14205 = 14205000
		b21000 = 21000000
	)

	cases := []struct {
		name           string
		rig            string
		main, fa, fb   int64
		vfo            string
		split          int64
		sticky         bool
		wantTX, wantRX int64
		wantSplit      bool
	}{
		// The reported failure: FTDX101D, SUB VFO pressed. OmniRig names VFO B and
		// reports it as the generic Freq; freqA still holds the main VFO. Preferring
		// freqA meant the display never followed the operator to B.
		{"FTDX101D on SUB VFO", "FTDX101D", b14205, a14200, b14205, "B", pmSplitOff, false, b14205, 0, false},
		{"FTDX101D on MAIN VFO", "FTDX101D", a14200, a14200, b14205, "A", pmSplitOff, false, a14200, 0, false},

		// Yaesu fallback for a rig file that names NO VFO at all (the stock FTDX10
		// one, confirmed 2026-07-26): the generic Freq matching FreqB is then the
		// only sign that the operator is on SUB. It applies ONLY when the enum is
		// silent — when the enum names a VFO, the enum wins (pair cases below).
		{"FTDX10, no enum, generic Freq is B", "FTDX10", b14205, a14200, b14205, "", pmSplitOff, false, b14205, 0, false},
		// Same alternating ini: one poll says OFF while the rig IS in split. The
		// latched ON flag has to survive the contradicting sample.
		{"FTDX101D split, contradicting OFF sample", "FTDX101D", a14200, a14200, b14205, "AB", pmSplitOff, true, b14205, a14200, true},
		// The latch must not manufacture a split out of a stale cross-band VFO B.
		{"FTDX101D latch, VFOs on different bands", "FTDX101D", a14200, a14200, b21000, "AB", pmSplitOff, true, a14200, 0, false},

		// Pair enums: first letter is the VFO being listened on. Reported by the
		// whole Yaesu family; ignoring them pinned the display to VFO A (F4NBZ,
		// 2026-07-26 — Log4OM follows SUB on the same rig through OmniRig).
		{"pair enum BA, simplex → listening on B", "", b14205, a14200, b14205, "BA", pmSplitOff, false, b14205, 0, false},
		{"pair enum BB, simplex → listening on B", "", b14205, a14200, b14205, "BB", pmSplitOff, false, b14205, 0, false},
		{"pair enum AB, simplex → listening on A", "", a14200, a14200, b14205, "AB", pmSplitOff, false, a14200, 0, false},
		{"pair enum AA, simplex → listening on A", "", a14200, a14200, b14205, "AA", pmSplitOff, false, a14200, 0, false},

		// F4?? IC-7610 report, 2026-07-27: the operator's custom rig file declares
		// VFO B permanently while they work on the MAIN VFO. OpsLog then wrote to A
		// (SetSimplexMode acts on main) and read B — "the frequency never follows
		// the knob". Honouring the enum is right in general, which is why the cure
		// is an explicit override in the settings, applied before this function.
		{"IC-7610 file wrongly says B — enum honoured, hence the override", "IC-7610", 0, 7150000, 14295000, "B", pmSplitOff, false, 14295000, 0, false},
		{"same rig with the override forcing A", "IC-7610", 0, 7150000, 14295000, "A", pmSplitOff, false, 7150000, 0, false},

		// Non-regression: a rig that does not report the VFO enum keeps the old
		// order — freqA, then the generic Freq, then freqB.
		{"no VFO enum, freqA populated (Yaesu/Kenwood)", "FT-891", a14200, a14200, 0, "", pmSplitOff, false, a14200, 0, false},
		{"no VFO enum, only generic Freq (IC-9100)", "IC-9100", a14200, 0, 0, "", pmSplitOff, false, a14200, 0, false},
		{"IC-7610: generic Freq reports B, enum says A", "IC-7610", b14205, a14200, b14205, "A", pmSplitOff, false, a14200, 0, false},

		// Split: PM_SPLITON must be read as a BIT. An exact == 0x8000 reported "no
		// split" for any rig that sets the flag alongside another bit.
		{"split, ON flag alone", "", a14200, a14200, b14205, "AB", pmSplitOn, false, b14205, a14200, true},
		{"split, ON flag with extra bits set", "", a14200, a14200, b14205, "AB", pmSplitOn | 0x40, false, b14205, a14200, true},
		{"listening on B → TX on A", "", b14205, a14200, b14205, "BA", pmSplitOn, false, a14200, b14205, true},

		// Split must NOT be inferred when the rig says OFF, nor from a stale VFO B
		// left on another band (the FT-710 / TS-570 false positive).
		{"OFF flag, two distinct VFOs", "", a14200, a14200, b14205, "A", pmSplitOff, false, a14200, 0, false},
		{"ON flag but VFOs on different bands", "", a14200, a14200, b21000, "AB", pmSplitOn, false, a14200, 0, false},
		{"ON flag but both VFOs identical", "", a14200, a14200, a14200, "AB", pmSplitOn, false, a14200, 0, false},
		{"ON and OFF both set — ambiguous, treat as no split", "", a14200, a14200, b14205, "A", pmSplitOn | pmSplitOff, false, a14200, 0, false},
	}

	for _, c := range cases {
		tx, rx, split := resolveOmniRigVFOs(c.rig, c.main, c.fa, c.fb, c.vfo, c.split, c.sticky)
		if tx != c.wantTX || rx != c.wantRX || split != c.wantSplit {
			t.Errorf("%s:\n  got  TX=%d RX=%d split=%v\n  want TX=%d RX=%d split=%v",
				c.name, tx, rx, split, c.wantTX, c.wantRX, c.wantSplit)
		}
	}
}

// Which VFO a frequency set writes to, and whether it may call SetSimplexMode.
//
// Reported on an FTDX101 (F4NBZ, 2026-07-29): listening on SUB, clicking a
// cluster spot dragged the radio back to MAIN. SetSimplexMode means "receive and
// transmit here, simplex" and OmniRig applies it to the MAIN VFO — so it must
// not be used when the operator is on SUB. They chose that VFO deliberately; a
// spot click asks for a frequency, not for a VFO change.
func TestOmniRigWriteTarget(t *testing.T) {
	// Mirrors the decision made in SetFrequency.
	target := func(vfo string) (prop string, simplexAllowed bool) {
		onSub := vfo == "B" || vfo == "BB" || vfo == "BA"
		if onSub {
			return "FreqB", false
		}
		return "FreqA", true
	}

	cases := []struct {
		vfo     string
		prop    string
		simplex bool
	}{
		// On the main VFO nothing changes: SetSimplexMode is what moves the Icoms
		// whose direct writes are ignored.
		{"", "FreqA", true},
		{"A", "FreqA", true},
		{"AA", "FreqA", true},
		{"AB", "FreqA", true},
		// On SUB, write FreqB and leave the VFO selection alone.
		{"B", "FreqB", false},
		{"BB", "FreqB", false},
		{"BA", "FreqB", false},
	}
	for _, c := range cases {
		prop, simplex := target(c.vfo)
		if prop != c.prop || simplex != c.simplex {
			t.Errorf("VFO %q → write %s, simplex=%v; want %s, simplex=%v",
				c.vfo, prop, simplex, c.prop, c.simplex)
		}
	}
}

// SetSimplexMode is skipped on Yaesu, and that is not a preference.
//
// On an FT-891 it returned OK on every spot click while the rig never moved. On
// an FT-2000 it did something worse — a log of one QSY shows the state going
// from Vfo="AB" split off to Vfo="BA" split ON, so the operator's reception
// jumped to VFO B and OpsLog, reading the radio correctly, displayed B. The
// direct FreqA/Freq write does the tuning on these rigs.
func TestYaesuRigsAreRecognisedForTheSimplexSkip(t *testing.T) {
	for _, yes := range []string{"FT-2000", "FT-891", "FTDX10", "ftdx101", " FT-991A "} {
		if !isYaesuRig(yes) {
			t.Errorf("%q not recognised as a Yaesu — SetSimplexMode would still be called on it", yes)
		}
	}
	// Everything else keeps the call: on Icom it is the authoritative one.
	for _, no := range []string{"IC-7610", "TS-590", "K3", "", "Flex-6600"} {
		if isYaesuRig(no) {
			t.Errorf("%q wrongly treated as a Yaesu", no)
		}
	}
}
