package main

import (
	"strings"
	"testing"
)

// TestCleanDescStripsJargon — the S3 acceptance criteria translated into
// regression cases. Any phrase listed here must not survive the sanitizer.
func TestCleanDescStripsJargon(t *testing.T) {
	cases := []struct {
		name    string
		in      string
		mustNot []string
	}{
		{
			name: "saving throw + DC",
			in:   "Each creature in the area must make a Dexterity saving throw (DC 15) or take damage.",
			mustNot: []string{
				"saving throw", "DC 15", "DC15",
			},
		},
		{
			name:    "saving throws plural",
			in:      "You have advantage on saving throws against spells.",
			mustNot: []string{"saving throws"},
		},
		{
			name:    "within range",
			in:      "Hurl a bolt at a creature within range.",
			mustNot: []string{"within range"},
		},
		{
			name:    "5-foot radius",
			in:      "Each creature in a 5-foot radius takes damage.",
			mustNot: []string{"5-foot"},
		},
		{
			name:    "spell slot upcast clause",
			in:      "When you cast this spell using a spell slot of 2nd level or higher, the damage increases.",
			mustNot: []string{"spell slot"},
		},
		{
			name:    "constitution score",
			in:      "Your Constitution score is 19 while you wear this amulet.",
			mustNot: []string{"Constitution score"},
		},
		{
			name:    "(save DC X) parenthetical",
			in:      "You can use an action to cast the detect thoughts spell (save DC 13) from it.",
			mustNot: []string{"(save", "DC 13"},
		},
		{
			name:    "wisdom save",
			in:      "Forces a wisdom save against the caster.",
			mustNot: []string{"wisdom save"},
		},
		{
			name:    "out to a range of feet",
			in:      "You have darkvision out to a range of 60 feet.",
			mustNot: []string{"feet", "out to a range"},
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := cleanDesc(tc.in)
			low := strings.ToLower(got)
			for _, ban := range tc.mustNot {
				if strings.Contains(low, strings.ToLower(ban)) {
					t.Errorf("cleanDesc kept banned phrase %q\n  in:  %q\n  out: %q", ban, tc.in, got)
				}
			}
		})
	}
}

// TestCleanDescRepairsOrphans — the strippers leave dangling stubs ("must
// make.", " and."); the post-pass is meant to tidy them so player-facing
// prose doesn't end mid-clause.
func TestCleanDescRepairsOrphans(t *testing.T) {
	in := "You gain a +1 bonus to ability checks and saving throws."
	got := cleanDesc(in)
	if strings.HasSuffix(got, " and.") {
		t.Errorf("orphan trailing 'and.' survived: %q", got)
	}
	in2 := "Up to three creatures of your choice that you can see must make charisma saving throws."
	got2 := cleanDesc(in2)
	if strings.Contains(strings.ToLower(got2), "must make.") {
		t.Errorf("orphan 'must make.' survived: %q", got2)
	}
}

// TestStripNameParenthetical — bestiary R21 / magic-item alias names.
func TestStripNameParenthetical(t *testing.T) {
	cases := map[string]string{
		"Giant Rat (Diseased)":           "Giant Rat",
		"Deep Gnome (Svirfneblin)":       "Deep Gnome",
		"Stone of Good Luck (Luckstone)": "Stone of Good Luck",
		"Ordinary Name":                  "Ordinary Name",
		"":                               "",
	}
	for in, want := range cases {
		if got := stripNameParenthetical(in); got != want {
			t.Errorf("stripNameParenthetical(%q) = %q, want %q", in, got, want)
		}
	}
}

// TestSpellOverrideWins — the curated descriptions for the SRD-only default
// spells must beat the auto first-sentence path.
func TestSpellOverrideWins(t *testing.T) {
	got := spellDescription("vicious_mockery", "anything could go here")
	if got != spellDescOverride["vicious_mockery"] {
		t.Errorf("override didn't win: got %q", got)
	}
	// Unknown ID falls through to cleanDesc.
	in := "Hurl a bolt at a creature within range."
	got = spellDescription("unknown_spell_xyz", in)
	if strings.Contains(got, "within range") {
		t.Errorf("fallthrough didn't sanitize: %q", got)
	}
}
