package dxped

import (
	"reflect"
	"testing"
	"time"
)

// Pinned against REAL feed text: the parser reads a fixed sentence, and a
// wrong split silently empties a DXpedition list rather than failing loudly.
func TestParseActivation(t *testing.T) {
	desc := "Aug 24-31, 2026 -- St Kitts and Nevis -- V47JA -- QSL: LoTW -- " +
		"Source: W5JON (Aug 1, 2026) -- By W5JON as V47JA fm Calypso Bay; 160-6m; SSB FT8; yagi, verticals"
	a := parseActivation(desc, "https://www.qrz.com/lookup/v47ja")
	if a == nil {
		t.Fatal("parseActivation returned nil on a real ADXO description")
	}
	if a.DXCC != "St Kitts and Nevis" {
		t.Errorf("DXCC = %q", a.DXCC)
	}
	if a.Callsign != "V47JA" {
		t.Errorf("Callsign = %q, want V47JA (mined from 'as')", a.Callsign)
	}
	if a.QSL != "LoTW" {
		t.Errorf("QSL = %q", a.QSL)
	}
	if a.StartDate != "Aug 24, 2026" || a.EndDate != "Aug 31, 2026" {
		t.Errorf("dates = %q..%q", a.StartDate, a.EndDate)
	}
	if want := []string{"160m", "6m"}; !reflect.DeepEqual(a.Bands, want) {
		t.Errorf("Bands = %v, want %v", a.Bands, want)
	}
	if want := []string{"SSB", "FT8"}; !reflect.DeepEqual(a.Modes, want) {
		t.Errorf("Modes = %v, want %v", a.Modes, want)
	}
}

// The callsign column often holds only the prefix; the operators prose holds
// the real thing, and a slashed call must lead with the DXCC prefix or no spot
// will ever match it.
func TestCallsAfterAsAndNormalise(t *testing.T) {
	if got := callsAfterAs("SQ2RAD as VP2EAD, M0PLX as VP2ELX"); !reflect.DeepEqual(got, []string{"VP2EAD", "VP2ELX"}) {
		t.Errorf("callsAfterAs = %v", got)
	}
	if got := normalizeCall("JG8NQJ/JD1", "JD1"); got != "JD1/JG8NQJ" {
		t.Errorf("normalizeCall = %q, want JD1/JG8NQJ", got)
	}
	if got := normalizeCall("PJ2/W2APF", "PJ2"); got != "PJ2/W2APF" {
		t.Errorf("normalizeCall rewrote an already-correct call: %q", got)
	}
}

func TestActivationStatus(t *testing.T) {
	now := time.Date(2026, 8, 27, 12, 0, 0, 0, time.UTC)
	cases := []struct{ start, end, want string }{
		{"Aug 24, 2026", "Aug 31, 2026", "active"},
		{"Sep 10, 2026", "Sep 20, 2026", "upcoming"},
		{"Aug 1, 2026", "Aug 10, 2026", "ended"},
		{"Aug 24, 2026", "Aug 27, 2026", "active"}, // ends TODAY: still on
		{"garbage", "garbage", "upcoming"},
	}
	for _, c := range cases {
		if got := activationStatus(c.start, c.end, now); got != c.want {
			t.Errorf("activationStatus(%q,%q) = %q, want %q", c.start, c.end, got, c.want)
		}
	}
}

// Mining a headline must find calls without inventing them out of jargon.
func TestCallsInHeadline(t *testing.T) {
	cases := []struct {
		title string
		want  []string
	}{
		{"3B7M, St Brandon", []string{"3B7M"}},
		{"TX5S team lands on Clipperton", []string{"TX5S"}},
		{"FT8 activity on 160m in 2026", nil},
		{"VP6D QSL via OQRS, LoTW", []string{"VP6D"}},
		{"JD1/JG8NQJ from Minami Torishima", []string{"JD1/JG8NQJ"}},
	}
	for _, c := range cases {
		if got := callsInHeadline(c.title); !reflect.DeepEqual(got, c.want) {
			t.Errorf("callsInHeadline(%q) = %v, want %v", c.title, got, c.want)
		}
	}
}
