package grammar_test

import (
	"strings"
	"testing"

	"chirami/internal/grammar"
)

func hasTermReasons(candidates []grammar.Result, expectedTerm string, checkRule bool, expectedRule string, checkReasons bool, expectedReasons []string) bool {
	for _, r := range candidates {
		if r.Base != expectedTerm {
			continue
		}
		if checkRule {
			if !r.MatchesCondition(expectedRule) {
				continue
			}
		}
		if checkReasons {
			if len(r.Steps) != len(expectedReasons) {
				continue
			}
			matched := true
			for i, reason := range expectedReasons {
				if r.Steps[i].Name != reason {
					matched = false
					break
				}
			}
			if !matched {
				continue
			}
		}
		return true
	}
	return false
}

// TestYomitanSuite runs the complete test suite ported from Yomitan's
// test/language/japanese-transforms.test.js.
func TestYomitanSuite(t *testing.T) {
	totalCases := 0
	passCount := 0
	validCount := 0
	invalidCount := 0

	for _, cat := range yomitanTestCategories {
		t.Run(cat.Category, func(t *testing.T) {
			for _, tc := range cat.Tests {
				totalCases++
				if cat.Valid {
					validCount++
				} else {
					invalidCount++
				}

				results := grammar.Deinflect(tc.Source)
				has := hasTermReasons(results, tc.Term, tc.CheckRule, tc.Rule, tc.CheckReasons, tc.Reasons)

				if has == cat.Valid {
					passCount++
				} else {
					t.Errorf("%s: source=%q expected base=%q rule=%q reasons=%v want valid=%v got has=%v",
						cat.Category, tc.Source, tc.Term, tc.Rule, tc.Reasons, cat.Valid, has)
				}
			}
		})
	}

	t.Logf("Yomitan test suite: %d/%d cases passed (valid: %d, invalid: %d)",
		passCount, totalCases, validCount, invalidCount)
}

func TestSuffixSplitting(t *testing.T) {
	cases := []struct {
		surface        string
		targetBase     string
		expectedSteps  []string
		expectedSuffix []string
		expectedStem   string
	}{
		{
			surface:        "食べさせられなかった",
			targetBase:     "食べる",
			expectedSteps:  []string{"causative", "potential or passive", "negative", "-た"},
			expectedSuffix: []string{"させ", "られ", "なかっ", "た"},
			expectedStem:   "食べ",
		},
		{
			surface:        "読んでいる",
			targetBase:     "読む",
			expectedSteps:  []string{"-て", "-いる"},
			expectedSuffix: []string{"んで", "いる"},
			expectedStem:   "読",
		},
		{
			surface:        "行ってきます",
			targetBase:     "行ってくる",
			expectedSteps:  []string{"-ます"},
			expectedSuffix: []string{"きます"},
			expectedStem:   "行って",
		},
		{
			surface:        "書かれた",
			targetBase:     "書く",
			expectedSteps:  []string{"passive", "-た"},
			expectedSuffix: []string{"かれ", "た"},
			expectedStem:   "書",
		},
		{
			surface:        "見たくない",
			targetBase:     "見る",
			expectedSteps:  []string{"-たい", "negative"},
			expectedSuffix: []string{"たく", "ない"},
			expectedStem:   "見",
		},
		{
			surface:        "来なかった",
			targetBase:     "来る",
			expectedSteps:  []string{"negative", "-た"},
			expectedSuffix: []string{"なかっ", "た"},
			expectedStem:   "来",
		},
		{
			surface:        "しなければならない",
			targetBase:     "しなければなる",
			expectedSteps:  []string{"negative"},
			expectedSuffix: []string{"らない"},
			expectedStem:   "しなければな",
		},
	}

	for _, tc := range cases {
		t.Run(tc.surface, func(t *testing.T) {
			results := grammar.Deinflect(tc.surface)

			var found *grammar.Result
			for i := range results {
				r := &results[i]
				if r.Base == tc.targetBase && len(r.Steps) == len(tc.expectedSteps) {
					match := true
					for j, name := range tc.expectedSteps {
						if r.Steps[j].Name != name {
							match = false
							break
						}
					}
					if match {
						found = r
						break
					}
				}
			}

			if found == nil {
				t.Fatalf("Deinflect(%q): target candidate Base=%q with steps=%v not found in %d results",
					tc.surface, tc.targetBase, tc.expectedSteps, len(results))
			}

			// Verify suffixes
			var actualSuffixes []string
			var totalSuffix strings.Builder
			for _, step := range found.Steps {
				actualSuffixes = append(actualSuffixes, step.Suffix)
				totalSuffix.WriteString(step.Suffix)
				if step.Description == "" && step.Name != "imperative negative slang" {
					t.Errorf("step %q missing description", step.Name)
				}
			}

			for j := range tc.expectedSuffix {
				if actualSuffixes[j] != tc.expectedSuffix[j] {
					t.Errorf("step %d (%s) suffix = %q, want %q",
						j, tc.expectedSteps[j], actualSuffixes[j], tc.expectedSuffix[j])
				}
			}

			// Verify base stem
			stem := found.BaseStem()
			if tc.expectedStem != "" && stem != tc.expectedStem {
				t.Errorf("BaseStem() = %q, want %q", stem, tc.expectedStem)
			}

			// Verify reconstruction
			spelled := stem + totalSuffix.String()
			if spelled != tc.surface {
				t.Errorf("stem + suffixes = %q, want surface %q", spelled, tc.surface)
			}
		})
	}
}

func BenchmarkDeinflect(b *testing.B) {
	word := "食べさせられなかった"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		res := grammar.Deinflect(word)
		_ = res
	}
}

func BenchmarkDeinflectShort(b *testing.B) {
	word := "食べた"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		res := grammar.Deinflect(word)
		_ = res
	}
}

func BenchmarkDeinflectMedium(b *testing.B) {
	word := "読んでいる"
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		res := grammar.Deinflect(word)
		_ = res
	}
}
