package main

import (
	"testing"

	"hamlog/internal/autocall"
)

// The entity's verdict is not the station's.
//
// From the air: on 10 m, where most countries are already in the log, the
// engine refused twenty decodes out of twenty-one as "worked" — a watched
// DXpedition calling CQ among them — because the ENTITY's status was read as
// the station's.
// allChased is the default: every orthogonal marker ticked.
var allChased = chaseExtras{pota: true, grid: true, pfx: true, county: true, state: true}

func TestCandidateWorkedIsTheCallsignNotTheEntity(t *testing.T) {
	d := autocall.Decode{Call: "J38DX", Band: "10m", Mode: "FT8", IsNew: true}

	// Grenada worked on 10 m FT8, this callsign never worked: nothing is needed
	// from it, and calling it is NOT a duplicate.
	c := candidateOf(d, SpotStatus{Status: "worked", WorkedSlot: false}, allChased)
	if c.Worked {
		t.Error("a station never worked was refused because its entity was")
	}
	if c.Need != autocall.NeedNone {
		t.Errorf("need = %v on a worked entity, want none", c.Need)
	}

	// The same callsign already in the log on this band and mode IS a duplicate.
	if c := candidateOf(d, SpotStatus{Status: "worked", WorkedSlot: true}, allChased); !c.Worked {
		t.Error("a callsign already worked on this band and mode was not flagged")
	}

	// And a real need still carries through, with the unconfirmed distinction.
	c = candidateOf(d, SpotStatus{Status: "new-band", UnconfStatus: true}, allChased)
	if c.Need != autocall.NeedBand || !c.Unconfirmed {
		t.Errorf("new-band unconfirmed came through as %v (unconf=%v)", c.Need, c.Unconfirmed)
	}
}

// A station whose ENTITY has nothing left to give can still be the reason the
// operator is on the band: a WPX prefix, a county, a square, a park that has
// never been worked. Reported from the air — auto-call sat through a
// never-worked prefix calling CQ and did nothing.
func TestOrthogonalMarkersAreWorthACall(t *testing.T) {
	d := autocall.Decode{Call: "BH2SWB", Band: "10m", Mode: "FT8", IsNew: true}
	worked := SpotStatus{Status: "worked"}

	for _, tc := range []struct {
		what string
		st   SpotStatus
		want string
	}{
		{"prefix", SpotStatus{Status: "worked", NewPfx: true}, "prefix"},
		{"county", SpotStatus{Status: "worked", NewCounty: true}, "county"},
		{"state", SpotStatus{Status: "worked", NewState: true}, "state"},
		{"square", SpotStatus{Status: "worked", NewGrid: true}, "square"},
		{"park", SpotStatus{Status: "worked", NewPOTA: true}, "park"},
	} {
		c := candidateOf(d, tc.st, allChased)
		if c.Need != autocall.NeedExtra || c.Extra != tc.want {
			t.Errorf("%s: need=%v extra=%q, want extra %q", tc.what, c.Need, c.Extra, tc.want)
		}
		// And not when the operator does not chase that kind of thing: the
		// switch that withdraws the badge withdraws the call with it.
		if c := candidateOf(d, tc.st, chaseExtras{}); c.Need != autocall.NeedNone {
			t.Errorf("%s: called for a marker the operator does not chase", tc.what)
		}
	}

	// It is the LOWEST rung: a real need on the entity still says what it is.
	if c := candidateOf(d, SpotStatus{Status: "new-band", NewPfx: true}, allChased); c.Need != autocall.NeedBand {
		t.Errorf("need = %v — a new band that is also a new prefix is a new band", c.Need)
	}
	// Nothing anywhere is still nothing.
	if c := candidateOf(d, worked, allChased); c.Need != autocall.NeedNone {
		t.Errorf("need = %v on a station with nothing to gain", c.Need)
	}
}
