package main

import (
	"testing"

	"hamlog/internal/award"
)

// The catalog is the channel through which a shipped award is both DELIVERED and
// CORRECTED. These tests pin the three rules that make that safe:
//   - a new catalog award reaches an operator who already has awards stored;
//   - a fixed one (higher Version) replaces the stored copy;
//   - unless the operator has edited it, in which case their work wins.

func catalogDef(t *testing.T, code string) award.Def {
	t.Helper()
	for _, d := range award.Defaults() {
		if d.Code == code {
			return d
		}
	}
	t.Fatalf("%s is not in the catalog", code)
	return award.Def{}
}

func findDef(defs []award.Def, code string) (award.Def, bool) {
	for _, d := range defs {
		if d.Code == code {
			return d, true
		}
	}
	return award.Def{}, false
}

func TestMergeCatalogAddsMissingAward(t *testing.T) {
	stored := []award.Def{{Code: "MYOWN", Name: "Mine", Valid: true}}
	got, updated, changed := mergeCatalog(stored)
	if !changed {
		t.Fatal("merge reported no change, but every catalog award was missing")
	}
	if len(updated) != 0 {
		t.Errorf("updated = %v, want none: an ADDED award is not an UPDATED one", updated)
	}
	if _, ok := findDef(got, "FFMA"); !ok {
		t.Error("FFMA was not added — a newly shipped award never reaches an existing install")
	}
	if _, ok := findDef(got, "MYOWN"); !ok {
		t.Error("the operator's own award was dropped by the merge")
	}
}

func TestMergeCatalogUpdatesOlderVersion(t *testing.T) {
	// The stored copy is an older revision of a shipped award, with a broken rule.
	old := catalogDef(t, "FFMA")
	old.Version = catalogDef(t, "FFMA").Version - 1
	old.Field = "wrong"
	old.Valid = false // the operator disabled it — a preference, not a definition

	got, updated, changed := mergeCatalog([]award.Def{old})
	if !changed || len(updated) == 0 {
		t.Fatal("a higher catalog version did not replace the stored definition — a shipped award could never be FIXED")
	}
	d, _ := findDef(got, "FFMA")
	if d.Field != "grid4" {
		t.Errorf("FFMA field = %q, want the catalog's %q", d.Field, "grid4")
	}
	if d.Valid {
		t.Error("the update re-enabled an award the operator had switched off; that is their choice to make, not ours")
	}
}

func TestMergeCatalogSkipsUserEdited(t *testing.T) {
	old := catalogDef(t, "FFMA")
	old.Version = catalogDef(t, "FFMA").Version - 1
	old.Field = "mine"
	old.UserEdited = true

	got, updated, _ := mergeCatalog([]award.Def{old})
	if len(updated) != 0 {
		t.Fatalf("updated = %v: an award the operator has edited must never be overwritten", updated)
	}
	if d, _ := findDef(got, "FFMA"); d.Field != "mine" {
		t.Errorf("field = %q, want the operator's %q", d.Field, "mine")
	}
}

func TestMarkUserEditedOnlyOnRealChange(t *testing.T) {
	prev := []award.Def{
		{Code: "A", Name: "A", Field: "state", Valid: true, Version: 2},
		{Code: "B", Name: "B", Field: "cqz", Valid: true, Version: 2},
	}
	next := []award.Def{
		{Code: "A", Name: "A", Field: "state", Valid: true},   // untouched
		{Code: "B", Name: "B", Field: "county", Valid: true},  // changed
		{Code: "C", Name: "C", Field: "note", Valid: true},    // brand new
	}
	markUserEdited(next, prev)

	if next[0].UserEdited {
		t.Error("A was flagged as edited although nothing about it changed — every save would freeze every award out of future updates")
	}
	if !next[1].UserEdited {
		t.Error("B changed field and was not flagged; a catalog update would overwrite the operator's work")
	}
	if next[2].UserEdited {
		t.Error("C is a brand-new award; there is no shipped version to protect it from")
	}
	// A save must not pretend to be a new shipped revision.
	if next[0].Version != 2 || next[1].Version != 2 {
		t.Errorf("versions = %d/%d, want both 2: saving is not shipping", next[0].Version, next[1].Version)
	}
}
