package claim

import (
	"reflect"
	"testing"
)

func TestTokensStripsPunctuationAndCase(t *testing.T) {
	got := Tokens("Какая сейчас погода в Риме?")
	want := []string{"какая", "сейчас", "погода", "в", "риме"}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Tokens = %q, want %q", got, want)
	}
}

// The dash forms matter: an STT transcript routinely carries "а, да, прости -
// на 9", and a stray dash counted as a token would dilute every coverage score
// in the sentence.
func TestTokensDropsBareDashes(t *testing.T) {
	got := Tokens("а, да, прости — на 9")
	want := []string{"а", "да", "прости", "на", "9"}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("Tokens = %q, want %q", got, want)
	}
}

func TestSplitPartitionsInOrder(t *testing.T) {
	consumed, unexplained := Split("напомни позвонить маме", "позвонить маме")
	if want := []string{"позвонить", "маме"}; !reflect.DeepEqual(consumed, want) {
		t.Errorf("consumed = %q, want %q", consumed, want)
	}
	if want := []string{"напомни"}; !reflect.DeepEqual(unexplained, want) {
		t.Errorf("unexplained = %q, want %q", unexplained, want)
	}
}

func TestCoverageIsZeroWithoutTokens(t *testing.T) {
	if got := (Claim{}).Coverage(); got != 0 {
		t.Errorf("Coverage of an empty claim = %v, want 0", got)
	}
}

func TestCoverageFraction(t *testing.T) {
	c := Claim{Consumed: []string{"a", "b", "c"}, Unexplained: []string{"d"}}
	if got := c.Coverage(); got != 0.75 {
		t.Errorf("Coverage = %v, want 0.75", got)
	}
}

// The band order is load-bearing, so it is asserted rather than assumed from
// the iota. BandUnknown must sit at the bottom: a claim whose builder forgot to
// set a band is a bug and must not outrank a measured one.
func TestBandOrder(t *testing.T) {
	ordered := []Band{BandUnknown, BandVetoed, BandNearest, BandStructural, BandAnchored}
	for i := 1; i < len(ordered); i++ {
		if !(ordered[i-1] < ordered[i]) {
			t.Errorf("%v is not below %v", ordered[i-1], ordered[i])
		}
	}
	for _, b := range ordered {
		if b.String() == "" {
			t.Errorf("band %d has no name", b)
		}
	}
	if BandUnknown.String() != "unknown" {
		t.Errorf("BandUnknown.String() = %q", BandUnknown.String())
	}
}

// The failure V-558 opened with, as an ordering test. A pending reminder eats
// "какая сейчас погода в Риме?" as a time answer and explains none of it; the
// weather source explains all of it. Coverage decides, and the band never gets
// consulted, which is the point: the pending claimant is structural and the
// weather claim is anchored, but even if the bands were equal the weather claim
// wins.
func TestCoverageBeatsBand(t *testing.T) {
	utterance := "какая сейчас погода в Риме?"
	pendingConsumed, pendingRest := Split(utterance, "сейчас")
	pending := Claim{
		Claimant: "reminder-followup", Intent: "reminder",
		Consumed: pendingConsumed, Unexplained: pendingRest,
		Band: BandStructural,
	}
	weatherConsumed, weatherRest := Split(utterance, utterance)
	weather := Claim{
		Claimant: "weather", Intent: "query",
		Consumed: weatherConsumed, Unexplained: weatherRest,
		Band: BandNearest,
	}
	if !weather.MoreSpecificThan(pending) {
		t.Errorf("weather (%v cover) did not beat pending (%v cover)",
			weather.Coverage(), pending.Coverage())
	}
	if pending.MoreSpecificThan(weather) {
		t.Error("pending beat weather, so the ordering is not asymmetric")
	}
}

// Where coverage ties, the band decides. Two grammars claiming the same
// utterance is the stage-0 contention case (ru-query-019 on the fixture), and
// today list order settles it with nothing recorded.
func TestBandBreaksACoverageTie(t *testing.T) {
	anchored := Claim{Claimant: "calendar-query", Consumed: []string{"a"}, Band: BandAnchored}
	nearest := Claim{Claimant: "classifier", Consumed: []string{"a"}, Band: BandNearest}
	if !anchored.MoreSpecificThan(nearest) {
		t.Error("anchored did not beat nearest on equal coverage")
	}
	if nearest.MoreSpecificThan(anchored) {
		t.Error("nearest beat anchored on equal coverage")
	}
}

func TestVetoed(t *testing.T) {
	if (Claim{}).Vetoed() {
		t.Error("a claim with no veto reports itself vetoed")
	}
	if !(Claim{Veto: "fact with no key"}).Vetoed() {
		t.Error("a claim with a veto reason does not report itself vetoed")
	}
}
