package watchlist

import (
	"encoding/json"
	"os"
	"path/filepath"
	"testing"
)

func TestGlobMatch(t *testing.T) {
	s := New(filepath.Join(t.TempDir(), "watchlist.json"))
	if err := s.Add("RI0SP*", false); err != nil {
		t.Fatal(err)
	}
	if err := s.Add("N8W", false); err != nil {
		t.Fatal(err)
	}
	// A starred entry is a family: the expedition's portable forms.
	for _, call := range []string{"RI0SP", "RI0SP/MM", "RI0SP/P"} {
		if _, ok := s.Match(call); !ok {
			t.Errorf("Match(%q) = false, want true", call)
		}
	}
	// A bare entry is THAT call — the reported bug was N8W lighting up for
	// N8WCR, a different station entirely.
	if _, ok := s.Match("N8W"); !ok {
		t.Error("exact entry must match its own call")
	}
	if _, ok := s.Match("N8WCR"); ok {
		t.Error("exact entry must NOT match a longer call")
	}
	if _, ok := s.Match("RI0S"); ok {
		t.Error("a SHORTER call must not match a starred entry")
	}
	if err := s.Add("VK*9", false); err == nil {
		t.Error("a mid-string star must be refused")
	}
	if err := s.Add("*", false); err == nil {
		t.Error("a bare star must be refused")
	}
}

func TestDXHunterFileRoundTrips(t *testing.T) {
	// A real DXHunter entry, ClubLog block included. It must survive
	// load → save byte-meaningfully: same keys, values preserved.
	src := `[{"callsign":"C5SP","lastSeen":"0001-01-01T00:00:00Z","lastSeenStr":"Never",
	  "addedAt":"2026-01-17T00:18:43.89Z","spotCount":7,"isContest":true,"notify":true,
	  "isExpedition":true,"clubLogQSOs24h":120,"clubLogTotalQSOs":30500,
	  "clubLogHasOQRS":true,"clubLogLiveStream":true,"clubLogUpdatedAt":"2026-08-28T22:32:18Z"}]`
	dir := t.TempDir()
	path := filepath.Join(dir, "watchlist.json")
	if err := os.WriteFile(path, []byte(src), 0o644); err != nil {
		t.Fatal(err)
	}
	s := New(path)
	s.Flush()
	out, err := os.ReadFile(path)
	if err != nil {
		t.Fatal(err)
	}
	var list []map[string]any
	if err := json.Unmarshal(out, &list); err != nil {
		t.Fatal(err)
	}
	if len(list) != 1 {
		t.Fatalf("got %d entries", len(list))
	}
	e := list[0]
	for k, want := range map[string]any{
		"callsign": "C5SP", "isContest": true, "notify": true,
		"isExpedition": true, "clubLogQSOs24h": float64(120),
		"clubLogTotalQSOs": float64(30500), "clubLogHasOQRS": true,
	} {
		if e[k] != want {
			t.Errorf("%s = %v, want %v", k, e[k], want)
		}
	}
}

func TestMarkSeenAndNotify(t *testing.T) {
	s := New(filepath.Join(t.TempDir(), "watchlist.json"))
	_ = s.Add("HB040A", false)
	_ = s.SetNotify("HB040A", true)
	entry, notify, ok := s.MarkSeen("HB040A")
	if !ok || !notify || entry != "HB040A" {
		t.Fatalf("MarkSeen = %q %v %v", entry, notify, ok)
	}
	list := s.Entries()
	if list[0].SpotCount != 1 || list[0].LastSeenStr != "Just now" {
		t.Errorf("entry after MarkSeen: %+v", list[0])
	}
}
