package textutil

import "testing"

// TestPluralize is a table-driven lock-in for the Pluralize() helper used
// to seed _System_Object.PluralLabel during managed-metadata bootstrap.
//
// The function existed without unit-test coverage; this test pins the
// observed behaviour of every documented branch. Each case below records
// what the algorithm returns today, not a linguistic-purity expectation
// — the canonical rule changes (if any) belong in a separate PR that
// also updates the matching SQL CASE in
// ensurePluralLabelsAndNameFieldsScoped (schema.go:2173).
//
// Backstop for NX#492: the legacy-naive-plural migration's
// SQL CASE mirrors this algorithm. If a future refactor changes one
// branch without the other, callers comparing
// MigrateLegacyNaivePlurals' output against Pluralize()'s output will
// drift; these table-driven cases are the lock-in that surfaces such a
// drift early.
func TestPluralize(t *testing.T) {
	cases := []struct {
		name  string
		input string
		want  string
	}{
		// Empty input: explicit short-circuit at the top of the function.
		{"empty_input", "", ""},

		// Default fall-through: no special suffix, append "s".
		{"default_account", "Account", "Accounts"},
		{"default_lead", "Lead", "Leads"},

		// Consonant + 'y' → strip 'y', append "ies". Anchored on
		// `prevChar` not in {a,e,i,o,u}.
		{"consonant_y_opportunity", "Opportunity", "Opportunities"},
		{"consonant_y_country", "Country", "Countries"},
		{"consonant_y_entity", "Entity", "Entities"},

		// Vowel + 'y' → falls through to default "+s". Anchored on
		// `prevChar` ∈ {a,e,i,o,u}.
		{"vowel_y_boy", "Boy", "Boys"},
		{"vowel_y_day", "Day", "Days"},

		// 'sh' / 'ch' endings → append "es".
		{"sh_dish", "Dish", "Dishes"},
		{"ch_branch", "Branch", "Branches"},

		// Sibilant 's' endings (ss / us / is) → append "es" via the
		// `prevChar ∈ {s,u,i}` branch. The branch returns
		// `label + "es"` (concatenation), NOT `label[:-2] + "es"`
		// (substitution), so "Crisis" → "Crisises" rather than the
		// linguistically-canonical "Crises". This test pins the
		// observed concatenation behaviour: changing the algorithm to
		// emit "Crises" would also need a coordinated update to the
		// matching SQL CASE in ensurePluralLabelsAndNameFieldsScoped
		// (and the migration in migrate_legacy_naive_plurals.go which
		// mirrors that CASE). Out of scope for NX#492, which only
		// corrects legacy `Label + "s"` rows; "Crisis" never showed up
		// as a legacy naive +s case ("Crisiss" doesn't match the SQL
		// CASE WHERE guard's CONCAT(Label, 's') predicate by accident
		// — the seed data simply doesn't include such a Label).
		{"sibilant_ss_address", "Address", "Addresses"},
		{"sibilant_us_bus", "Bus", "Buses"},
		{"sibilant_us_status", "Status", "Statuses"},
		{"sibilant_is_crisis_concatenation_not_substitution", "Crisis", "Crisises"},

		// Non-sibilant 's' (e.g. preceded by a consonant other than
		// {s,u,i}) → already plural, return label unchanged. Note this
		// diverges from the SQL CASE in
		// ensurePluralLabelsAndNameFieldsScoped which always appends
		// "es" for any '$s' Label; in practice _System_Object Labels do
		// not arrive with this shape (standard CRM objects are singular,
		// custom objects rarely end in 's'), so the divergence is
		// theoretical. NX#492 design doc carries this caveat.
		{"non_sibilant_s_cars", "Cars", "Cars"},

		// 'x' ending → append "es".
		{"x_box", "Box", "Boxes"},

		// Single-z ending → append "zes" (Quiz → Quizzes).
		{"z_quiz", "Quiz", "Quizzes"},

		// Already-doubled 'zz' → append "es" (Buzz → Buzzes), via the
		// `n >= 2 && lower[n-2] == 'z'` short-circuit.
		{"zz_buzz", "Buzz", "Buzzes"},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := Pluralize(tc.input)
			if got != tc.want {
				t.Fatalf("Pluralize(%q) = %q, want %q", tc.input, got, tc.want)
			}
		})
	}
}
