package main

import (
	"context"
	"path/filepath"
	"testing"
	"time"

	"hamlog/internal/db"
	"hamlog/internal/dxcc"
	"hamlog/internal/qso"
	"hamlog/internal/settings"
)

// The operator's exact case, end to end through the real verdict path.
//
// South Africa is confirmed (LoTW) on 20m and worked-but-unconfirmed on 15m,
// with the hunt set to "new + unconfirmed" over LoTW and paper QSL. A 15m FT8
// decode must come back NEW BAND — that is what the settings ask — and carry
// the UNCONFIRMED flag, which is what draws the badge as a missing QSL rather
// than a band never worked.
func TestClusterStatusFlagsUnconfirmedBand(t *testing.T) {
	dir := t.TempDir()
	conn, err := db.Open(filepath.Join(dir, "log.db"))
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	t.Cleanup(func() { conn.Close() })

	a := &App{ctx: context.Background(), qso: qso.NewRepo(conn), settings: settings.NewStore(conn)}
	a.settingsScoped.Store(true)
	if err := a.settings.Set(a.ctx, keyChaseMode, "new_unconfirmed"); err != nil {
		t.Fatalf("set chase mode: %v", err)
	}
	if err := a.settings.Set(a.ctx, keyChaseConfirm, "lotw,card"); err != nil {
		t.Fatalf("set chase sources: %v", err)
	}

	// The real prefix table, so ZS4AW resolves the way it does in the app.
	a.dxcc = dxcc.NewManager(filepath.Join("build", "bin", "data"))
	if err := a.dxcc.LoadFromDisk(); err != nil {
		t.Skipf("cty.dat not available here: %v", err)
	}

	za := 462
	add := func(call, band, mode, lotw string) {
		hz := int64(21074000)
		if _, err := a.qso.Add(a.ctx, qso.QSO{
			Callsign: call, Band: band, Mode: mode, FreqHz: &hz,
			QSODate: time.Now().UTC().Add(-72 * time.Hour),
			DXCC:    &za, Country: "South Africa", LOTWRcvd: lotw,
		}); err != nil {
			t.Fatalf("add %s: %v", call, err)
		}
	}
	add("ZS1AAA", "20m", "FT8", "Y") // the entity IS confirmed, elsewhere
	add("ZS6GAV", "15m", "FT8", "N") // 15m worked, never confirmed
	add("ZS4AW", "15m", "FT8", "N")

	// A station of the entity that is NOT the one already worked on this slot:
	// that case has a rule of its own, tested below.
	got := a.ClusterSpotStatuses([]SpotQuery{{Call: "ZS9XX", Band: "15m", Mode: "FT8"}})
	if len(got) != 1 {
		t.Fatalf("got %d results", len(got))
	}
	if got[0].Status != "new-band" {
		t.Fatalf("status = %q, want new-band", got[0].Status)
	}
	if !got[0].UnconfStatus {
		t.Error("UnconfStatus is false — the badge draws solid, as if 15m had never been worked")
	}
}

// The same station, already worked on the same band and mode, has nothing left
// to give: working it again cannot turn a missing QSL into a confirmation. The
// need belongs to the entity, not to that callsign, so the badge goes quiet on
// it — and stays on every other station of the entity.
func TestWorkedSameSlotDropsTheUnconfirmedBadge(t *testing.T) {
	dir := t.TempDir()
	conn, err := db.Open(filepath.Join(dir, "log.db"))
	if err != nil {
		t.Fatalf("open: %v", err)
	}
	t.Cleanup(func() { conn.Close() })

	a := &App{ctx: context.Background(), qso: qso.NewRepo(conn), settings: settings.NewStore(conn)}
	a.settingsScoped.Store(true)
	_ = a.settings.Set(a.ctx, keyChaseMode, "new_unconfirmed")
	_ = a.settings.Set(a.ctx, keyChaseConfirm, "lotw,card")
	a.dxcc = dxcc.NewManager(filepath.Join("build", "bin", "data"))
	if err := a.dxcc.LoadFromDisk(); err != nil {
		t.Skipf("cty.dat not available here: %v", err)
	}

	hz := int64(10136000)
	if _, err := a.qso.Add(a.ctx, qso.QSO{
		Callsign: "TN8GD", Band: "30m", Mode: "FT8", FreqHz: &hz,
		QSODate: time.Now().UTC().Add(-24 * time.Hour),
	}); err != nil {
		t.Fatalf("add: %v", err)
	}

	got := a.ClusterSpotStatuses([]SpotQuery{
		{Call: "TN8GD", Band: "30m", Mode: "FT8"}, // the very station worked
		{Call: "TN4XY", Band: "30m", Mode: "FT8"}, // another in the same entity
	})
	if got[0].Status == "new" || got[0].UnconfStatus {
		t.Errorf("the worked station still advertises a need: status=%q unconf=%v",
			got[0].Status, got[0].UnconfStatus)
	}
	if got[1].Status != "new" {
		t.Errorf("another station in the entity lost its badge: status=%q", got[1].Status)
	}
}
