package award

import (
	"strings"
	"testing"

	"hamlog/internal/qso"
)

// An operator's correction must survive a recompute.
//
// Reported on the DARC DOK award: a German address matches two or three DOKs,
// the operator deletes the wrong ones and assigns the right one, and the next
// recompute puts the wrong ones straight back. The manual reference was being
// ADDED to what the matcher found instead of replacing it, so the correction
// could never win.
func TestManualReferenceReplacesTheMatch(t *testing.T) {
	// An award that reads the address and matches a reference's description —
	// the shape the DOK award uses, and the shape that over-matches.
	def := Def{
		Code: "DLD", Name: "DARC DOK", Field: "address",
		MatchBy: "description", Type: "QSOFIELDS", Valid: true,
	}
	metas := []RefMeta{
		{Code: "A01", Name: "Berlin", Valid: true},
		{Code: "B05", Name: "Berlin Mitte", Valid: true},
	}
	// An address that legitimately matches both.
	q := &qso.QSO{
		Callsign: "DL2FDM",
		Address:  "Berlin Mitte, 10115 Berlin",
	}

	auto := Explain(def, metas, q)
	if len(auto.Result) < 2 {
		t.Fatalf("the test needs an over-matching case; got %v", auto.Result)
	}

	// The operator picks the right one.
	q.Extras = map[string]string{ManualRefsKey: "DLD@B05"}
	fixed := Explain(def, metas, q)

	if got := strings.Join(fixed.Result, ","); got != "B05" {
		t.Errorf("after the correction the QSO counts for %q — want B05 alone", got)
	}
	if len(fixed.Superseded) == 0 {
		t.Error("the trace does not say what the correction replaced")
	}

	// A QSO with no correction still gets everything the matcher found: the
	// override must not disable matching for everyone else.
	q2 := &qso.QSO{Callsign: "DL3XYZ", Address: "Berlin Mitte, 10115 Berlin"}
	if len(Explain(def, metas, q2).Result) < 2 {
		t.Error("an untouched QSO lost its automatic references")
	}
}

// On an award where a QSO can only count for ONE reference, an ambiguous match
// assigns none.
//
// Two DARC DOKs are called "Gießen" because two clubs share the town. Exact
// matching finds both, and both are right as far as the matcher can tell.
// Picking one would write a reference into the log that may well be the other —
// so the contact is left for the operator, who now has the last word.
func TestAmbiguousMatchIsRefusedWhenOnlyOneReferenceIsAllowed(t *testing.T) {
	def := Def{
		Code: "DLD", Name: "DARC DOK", Field: "qth",
		MatchBy: "description", ExactMatch: true, Type: "QSOFIELDS", Valid: true,
		OneRefPerQSO: true,
	}
	metas := []RefMeta{
		{Code: "F07", Name: "Gießen", Valid: true},
		{Code: "F61", Name: "Gießen", Valid: true},
		{Code: "B11", Name: "Kassel", Valid: true},
	}

	amb := Explain(def, metas, &qso.QSO{Callsign: "DL2FDM", QTH: "Gießen"})
	if len(amb.Result) != 0 {
		t.Errorf("an ambiguous match produced %v — it should produce nothing", amb.Result)
	}
	if len(amb.Ambiguous) != 2 {
		t.Errorf("the trace lists %v — it should name both candidates", amb.Ambiguous)
	}

	// An unambiguous one is untouched: this must not make the award stricter in
	// general, only where the data genuinely does not decide.
	ok := Explain(def, metas, &qso.QSO{Callsign: "DL3XYZ", QTH: "Kassel"})
	if len(ok.Result) != 1 || ok.Result[0] != "B11" {
		t.Errorf("an unambiguous QSO gave %v — want B11", ok.Result)
	}

	// And the operator's choice still wins over the ambiguity.
	fixed := Explain(def, metas, &qso.QSO{
		Callsign: "DL2FDM", QTH: "Gießen",
		Extras: map[string]string{ManualRefsKey: "DLD@F61"},
	})
	if len(fixed.Result) != 1 || fixed.Result[0] != "F61" {
		t.Errorf("after the operator picked F61 the QSO counts for %v", fixed.Result)
	}
}
