package misspell

import (
	"cmp"
	"slices"
	"sort"
	"testing"
)

type sortByLen []string

func (a sortByLen) Len() int      { return len(a) }
func (a sortByLen) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a sortByLen) Less(i, j int) bool {
	if len(a[i]) == len(a[j]) {
		// if words are same size, then use
		// normal alphabetical order
		return a[i] < a[j]
	}
	// INVERTED  -- biggest words first
	return len(a[i]) > len(a[j])
}

func Test_word_sort(t *testing.T) {
	testCases := []struct {
		desc string
		dict []string
	}{
		{
			desc: "main",
			dict: DictMain,
		},
		{
			desc: "US",
			dict: DictAmerican,
		},
		{
			desc: "UK",
			dict: DictBritish,
		},
	}

	for _, test := range testCases {
		t.Run(test.desc, func(t *testing.T) {
			t.Parallel()

			if len(test.dict)%2 == 1 {
				t.Errorf("Dictionary is not a multiple of 2")
			}

			words := make([]string, 0, len(test.dict)/2)
			for i := 0; i < len(test.dict); i += 2 {
				words = append(words, test.dict[i])
			}

			if !sort.IsSorted(sortByLen(words)) {
				t.Errorf("Words not sorted by len, by alpha!")
				t.Errorf("Words.go is autogenerated -- do not edit.")
				t.Errorf("File issue instead.")
			}
		})
	}
}

type Tuple struct {
	Typo       string
	Correction string
}

func Test_consistent_dictionaries(t *testing.T) {
	testCases := []struct {
		desc string
		dict []string
	}{
		{
			desc: "main",
			dict: DictMain,
		},
		{
			desc: "US",
			dict: DictAmerican,
		},
		{
			desc: "UK",
			dict: DictBritish,
		},
	}

	for _, test := range testCases {
		t.Run(test.desc, func(t *testing.T) {
			t.Parallel()

			var inconsistentWords []Tuple

			incorrectWords := make(map[string]string)
			for i := 0; i < len(test.dict); i += 2 {
				incorrectWords[test.dict[i]] = test.dict[i+1]
			}

			for i := 1; i < len(test.dict); i += 2 {
				if incorrectWords[test.dict[i]] != "" {
					inconsistentWords = append(inconsistentWords,
						Tuple{Typo: test.dict[i-1], Correction: test.dict[i]},
						Tuple{Typo: test.dict[i], Correction: incorrectWords[test.dict[i]]},
					)

					t.Errorf("%s is listed as both typo and correction (%s)", test.dict[i], test.desc)
				}
			}

			slices.SortFunc(inconsistentWords, func(a, b Tuple) int {
				return cmp.Compare(a.Correction, b.Correction)
			})

			uniq := slices.CompactFunc(inconsistentWords, func(a, b Tuple) bool {
				return a.Typo == b.Typo && a.Correction == b.Correction
			})

			for _, tuple := range uniq {
				t.Logf("%s\t\t%s\n", tuple.Typo, tuple.Correction)
			}
		})
	}
}
