package main

import (
	"testing"

	"hamlog/internal/award"
)

// relabelOnly decides whether a save can take the cheap path. A wrong "yes"
// leaves stale references in the log, so every case that is not purely a
// display change must fall through to the full recompute.
func TestRelabelOnly(t *testing.T) {
	base := []award.Def{
		{Code: "DDFM", Field: "note", RefDisplay: "ref", Valid: true},
		{Code: "IOTA", Field: "iota", RefDisplay: "", Valid: true},
	}
	cp := func() []award.Def { out := make([]award.Def, len(base)); copy(out, base); return out }

	// The case this exists for: one award's column switches to the description.
	next := cp()
	next[0].RefDisplay = "name"
	codes, ok := relabelOnly(base, next)
	if !ok || len(codes) != 1 || codes[0] != "DDFM" {
		t.Errorf("display-only change = (%v, %v), want ([DDFM], true)", codes, ok)
	}

	// Two at once is still a relabel.
	next = cp()
	next[0].RefDisplay = "both"
	next[1].RefDisplay = "name"
	if codes, ok := relabelOnly(base, next); !ok || len(codes) != 2 {
		t.Errorf("two display changes = (%v, %v), want two codes", codes, ok)
	}

	// Nothing changed: no relabel to do, and no full recompute claimed either.
	if codes, ok := relabelOnly(base, cp()); ok || codes != nil {
		t.Errorf("identical definitions must not report a relabel, got (%v, %v)", codes, ok)
	}

	// Anything that can change WHICH QSOs match must fail the test.
	for name, mutate := range map[string]func([]award.Def){
		"field":     func(d []award.Def) { d[0].Field = "comment" },
		"pattern":   func(d []award.Def) { d[0].Pattern = `\d{2}` },
		"exact":     func(d []award.Def) { d[0].ExactMatch = true },
		"validity":  func(d []award.Def) { d[0].ValidFrom = "2020-01-01" },
		"dxcc":      func(d []award.Def) { d[0].DXCCFilter = []int{227} },
		"disabled":  func(d []award.Def) { d[0].Valid = false },
		"or rule":   func(d []award.Def) { d[0].OrRules = []award.OrRule{{Field: "qth"}} },
		"with disp": func(d []award.Def) { d[0].RefDisplay = "name"; d[0].Field = "comment" },
	} {
		n := cp()
		mutate(n)
		if _, ok := relabelOnly(base, n); ok {
			t.Errorf("%s: took the relabel path — stale references would stay in the log", name)
		}
	}

	// An award added or removed changes the set, never a relabel.
	if _, ok := relabelOnly(base, append(cp(), award.Def{Code: "WWFF"})); ok {
		t.Errorf("an added award must force a full recompute")
	}
	if _, ok := relabelOnly(base, cp()[:1]); ok {
		t.Errorf("a removed award must force a full recompute")
	}
}
