package award

import (
	"testing"

	"hamlog/internal/qso"
)

// Every source the editor offers must actually do something. "qrzcom" and
// "custom" were listed in the UI and in Def's doc comment but had no case in the
// switch, so ticking either marked nothing as confirmed — a checkbox that is
// trusted and silently inert.
func TestConfirmedSources(t *testing.T) {
	cases := []struct {
		name    string
		q       qso.QSO
		d       Def
		sources []string
		want    bool
	}{
		{"lotw", qso.QSO{LOTWRcvd: "Y"}, Def{}, []string{"lotw"}, true},
		{"qsl", qso.QSO{QSLRcvd: "Y"}, Def{}, []string{"qsl"}, true},
		{"eqsl", qso.QSO{EQSLRcvd: "Y"}, Def{}, []string{"eqsl"}, true},

		// QRZ confirms on the DOWNLOAD status. The upload one is us telling QRZ
		// about the QSO, which is not a confirmation of anything.
		{"qrz download", qso.QSO{QRZComDownloadStatus: "Y"}, Def{}, []string{"qrzcom"}, true},
		{"qrz upload only", qso.QSO{QRZComUploadStatus: "Y"}, Def{}, []string{"qrzcom"}, false},

		// Custom, no value required: any non-empty value counts. This is the
		// OpsLog card case — the marker holds a timestamp, not a flag.
		{"custom any value",
			qso.QSO{Extras: map[string]string{"APP_OPSLOG_QSL_RCVD": "2026-08-09T01:00:00Z"}},
			Def{ConfirmField: "APP_OPSLOG_QSL_RCVD"}, []string{"custom"}, true},
		{"custom field empty",
			qso.QSO{Extras: map[string]string{"APP_OPSLOG_QSL_RCVD": ""}},
			Def{ConfirmField: "APP_OPSLOG_QSL_RCVD"}, []string{"custom"}, false},

		// Custom with an explicit value list, matched case-insensitively.
		{"custom value match",
			qso.QSO{Extras: map[string]string{"APP_CLUB_CONF": "v"}},
			Def{ConfirmField: "APP_CLUB_CONF", ConfirmValue: "Y,V"}, []string{"custom"}, true},
		{"custom value mismatch",
			qso.QSO{Extras: map[string]string{"APP_CLUB_CONF": "N"}},
			Def{ConfirmField: "APP_CLUB_CONF", ConfirmValue: "Y,V"}, []string{"custom"}, false},

		// A custom source naming no field must confirm NOTHING — never everything.
		{"custom without a field",
			qso.QSO{Extras: map[string]string{"APP_OPSLOG_QSL_RCVD": "x"}},
			Def{}, []string{"custom"}, false},

		// Known QSO fields resolve too, not just extras.
		{"custom on a known field", qso.QSO{State: "TX"},
			Def{ConfirmField: "state", ConfirmValue: "TX"}, []string{"custom"}, true},

		// Any source in the list is enough.
		{"first source misses, second hits", qso.QSO{EQSLRcvd: "Y"}, Def{},
			[]string{"lotw", "eqsl"}, true},
		{"no source set", qso.QSO{LOTWRcvd: "Y"}, Def{}, nil, false},
	}
	for _, c := range cases {
		if got := Confirmed(&c.q, c.d, c.sources); got != c.want {
			t.Errorf("%s: Confirmed = %v, want %v", c.name, got, c.want)
		}
	}
}

// HAMLOG.online is a named confirmation source, not something to express through
// "custom": an award ticks it the way it ticks LoTW.
func TestHamlogConfirmationSource(t *testing.T) {
	def := Def{Confirm: []string{"hamlog"}, Validate: []string{"hamlog"}}
	yes := func(extras map[string]string) bool {
		return Confirmed(&qso.QSO{Extras: extras}, def, def.Confirm)
	}
	if !yes(map[string]string{HamlogQSLKey: "Y"}) {
		t.Error("our own key did not confirm")
	}
	// Their export's field name is unpublished, so the plausible shapes count too.
	if !yes(map[string]string{"APP_HAMLOG_QSL": "20260823"}) {
		t.Error("a date in an alternative key did not confirm")
	}
	// A value is not a confirmation when it says no.
	if yes(map[string]string{HamlogQSLKey: "N"}) {
		t.Error(`"N" was taken as a confirmation`)
	}
	if yes(map[string]string{HamlogQSLKey: "  "}) {
		t.Error("blank was taken as a confirmation")
	}
	if yes(nil) || yes(map[string]string{"SOMETHING_ELSE": "Y"}) {
		t.Error("confirmed with nothing to confirm it")
	}
	// And it does not leak into an award that never asked for it.
	other := Def{Confirm: []string{"lotw"}}
	if Confirmed(&qso.QSO{Extras: map[string]string{HamlogQSLKey: "Y"}}, other, other.Confirm) {
		t.Error("a hamlog confirmation counted for an award that only accepts LoTW")
	}
}

// The field name is theirs, taken from a real export:
//
//	<APP_HAMLOG_QSO_CFM:1>Y
//
// Pinned as a test because it was GUESSED wrong first — an import that carries
// its confirmations into OpsLog with nothing to rename is the whole point, and
// a renamed constant would break it in silence.
func TestHamlogUsesTheSitesOwnFieldName(t *testing.T) {
	if HamlogQSLKey != "APP_HAMLOG_QSO_CFM" {
		t.Fatalf("HamlogQSLKey = %q — their export writes APP_HAMLOG_QSO_CFM", HamlogQSLKey)
	}
	def := Def{Confirm: []string{"hamlog"}}
	// The record as HAMLOG exports it.
	q := &qso.QSO{Extras: map[string]string{
		"APP_HAMLOG_R150COUNTRY": "Russia",
		"APP_HAMLOG_QSO_CFM":     "Y",
	}}
	if !Confirmed(q, def, def.Confirm) {
		t.Error("a real HAMLOG export did not confirm")
	}
	// Anything OpsLog stamped before that export existed keeps counting.
	old := &qso.QSO{Extras: map[string]string{"APP_OPSLOG_HAMLOG_QSL": "Y"}}
	if !Confirmed(old, def, def.Confirm) {
		t.Error("a QSO stamped with the older key stopped counting")
	}
}
