package awardref

import (
	"strings"
	"testing"
	"unicode/utf8"

	"hamlog/internal/award"
)

// TestUSCountyRefsSelfConsistent is the guard that was missing when the Doña Ana
// row shipped mangled: its code was "NM/DO̱AANA", which no log, no callbook and
// no county database could ever produce, so that county was unwinnable and
// nothing said so.
//
// Every reference must reproduce its own code from its own display name through
// award.USCountyKey — the same function cmd/cntygen used to build it. That ties
// the generated file to the matcher: change the normalisation rules and this
// fails the moment one of the 3 102 references stops matching itself.
func TestUSCountyRefsSelfConsistent(t *testing.T) {
	refs := usCounties()
	if len(refs) < 3000 {
		t.Fatalf("only %d counties — the generated list looks truncated", len(refs))
	}
	for _, r := range refs {
		if !utf8.ValidString(r.Name) || !utf8.ValidString(r.Code) {
			t.Errorf("%s (%q): not valid UTF-8", r.Code, r.Name)
			continue
		}
		// Name is "County Name, ST".
		i := strings.LastIndex(r.Name, ", ")
		if i < 0 {
			t.Errorf("%s: name %q is not \"County, ST\"", r.Code, r.Name)
			continue
		}
		name, st := r.Name[:i], r.Name[i+2:]
		if got := award.USCountyKey(st, name); got != r.Code {
			t.Errorf("USCountyKey(%q, %q) = %q, want %q", st, name, got, r.Code)
		}
	}
}

// TestUSCountyRefsUnique catches a normalisation rule loose enough to collapse
// two real counties onto one key — the failure mode that would silently merge
// them in every award total.
func TestUSCountyRefsUnique(t *testing.T) {
	seen := map[string]string{}
	for _, r := range usCounties() {
		if prev, dup := seen[r.Code]; dup {
			t.Errorf("code %s is shared by %q and %q", r.Code, prev, r.Name)
			continue
		}
		seen[r.Code] = r.Name
	}
}

// TestUSCountyResolverShapes pins the spellings OpsLog's own offline resolver
// produces (GeoNames, via internal/uls) against the reference. These are the
// real strings that were failing to match, with the number of US callsigns each
// covers in a full FCC ULS import.
func TestUSCountyResolverShapes(t *testing.T) {
	valid := map[string]bool{}
	for _, r := range usCounties() {
		valid[r.Code] = true
	}
	cases := []struct {
		st, county, want string
	}{
		{"CA", "City and County of San Francisco", "CA/SANFRANCISCO"}, // 4 217 calls
		{"NM", "Doña Ana", "NM/DONAANA"},                              // 1 214
		{"NM", "Dona Ana", "NM/DONAANA"},                              // what logs hold
		{"MD", "Baltimore (city)", "MD/BALTIMORECITY"},                // 885
		{"MO", "St. Louis (city)", "MO/STLOUISCITY"},                  // 574
		{"MO", "St. Louis", "MO/STLOUIS"},                             // the county, distinct
		{"AK", "Nome (CA)", "AK/NOME"},                                // 252
		{"AK", "Yukon-Koyukuk (CA)", "AK/YUKONKOYUKUK"},               // 84
		{"AK", "City and Borough of Wrangell", "AK/WRANGELLCITYAND"},  // 27
		{"AK", "Juneau", "AK/JUNEAUCITYAND"},                          // a log that just says Juneau
		{"AK", "Kusilvak", "AK/WADEHAMPTON"},                          // renamed 2015
		{"SD", "Oglala Lakota County", "SD/SHANNON"},                  // renamed 2015
		{"AK", "Chugach Census Area", "AK/VALDEZCORDOVA"},             // split 2019
		{"AK", "Copper River Census Area", "AK/VALDEZCORDOVA"},
		{"CO", "City and County of Denver", "CO/DENVER"},
		{"HI", "City and County of Honolulu", "HI/HONOLULU"},
	}
	for _, c := range cases {
		got := award.USCountyKey(c.st, c.county)
		if got != c.want {
			t.Errorf("USCountyKey(%q, %q) = %q, want %q", c.st, c.county, got, c.want)
			continue
		}
		if !valid[got] {
			t.Errorf("USCountyKey(%q, %q) = %q, which is not in the reference list", c.st, c.county, got)
		}
	}
}

// TestUSCountyKeepsVirginiaCitiesOut guards the other direction: cmd/cntygen
// drops Virginia's independent cities and Carson City on purpose, because CQ's
// USA-CA does not count them. Normalising their names must not smuggle them
// back in as some neighbouring county.
func TestUSCountyKeepsVirginiaCitiesOut(t *testing.T) {
	valid := map[string]bool{}
	for _, r := range usCounties() {
		valid[r.Code] = true
	}
	for _, c := range []string{"Virginia Beach (city)", "City of Alexandria", "Lynchburg (city)"} {
		if k := award.USCountyKey("VA", c); valid[k] {
			t.Errorf("%q resolved to %s, which USA-CA does not count", c, k)
		}
	}
	// Charles City and James City ARE counties, despite the name.
	for _, c := range []string{"Charles City", "James City"} {
		if k := award.USCountyKey("VA", c); !valid[k] {
			t.Errorf("%q resolved to %s, which is missing from the reference", c, k)
		}
	}
}
