package tts

import (
	"os"
	"path/filepath"
	"testing"
)

func TestLexiconRewritesNames(t *testing.T) {
	lex := NewLexicon(map[string]string{
		"Vikunja":        "Викунья",
		"Home Assistant": "Хоум Ассистент",
		"Home":           "Хоум",
		"GPU":            "джи-пи-ю",
	})
	for _, tc := range []struct{ in, want string }{
		{"задача в Vikunja готова", "задача в Викунья готова"},
		// Case-insensitive: the router and the model both change the case of a
		// name on the way through.
		{"открой vikunja.", "открой Викунья."},
		// Longest first, or "Home Assistant" is read as "Хоум Assistant".
		{"Home Assistant не отвечает", "Хоум Ассистент не отвечает"},
		// Two names in a row share the space between them, which one pass
		// would consume.
		{"GPU GPU", "джи-пи-ю джи-пи-ю"},
		// Two passes cover a run of any length, because the first pass takes
		// every other name and leaves both boundaries of the ones it skipped.
		{"GPU GPU GPU GPU", "джи-пи-ю джи-пи-ю джи-пи-ю джи-пи-ю"},
		// Not a word boundary: a name inside a longer token is left alone.
		{"vikunjaless", "vikunjaless"},
		{"ничего не совпало", "ничего не совпало"},
		{"", ""},
	} {
		if got := lex.Apply(tc.in); got != tc.want {
			t.Errorf("Apply(%q) = %q, want %q", tc.in, got, tc.want)
		}
	}
}

// The zero value and an unconfigured path rewrite nothing, so a daemon with no
// dictionary behaves as it did before this existed.
func TestLexiconOffByDefault(t *testing.T) {
	var zero *Lexicon
	if got := zero.Apply("Vikunja"); got != "Vikunja" {
		t.Errorf("nil lexicon rewrote %q", got)
	}
	lex, err := LoadLexicon("")
	if err != nil {
		t.Fatalf("LoadLexicon(\"\"): %v", err)
	}
	if lex.Size() != 0 || lex.Apply("Vikunja") != "Vikunja" {
		t.Errorf("empty path produced a live lexicon of %d names", lex.Size())
	}
}

// A path he set and that cannot be read is a startup failure. Saying names
// wrong in silence is what the dictionary exists to stop.
func TestLexiconLoadErrors(t *testing.T) {
	if _, err := LoadLexicon(filepath.Join(t.TempDir(), "nope.json")); err == nil {
		t.Error("a missing dictionary must be an error")
	}
	bad := filepath.Join(t.TempDir(), "bad.json")
	if err := os.WriteFile(bad, []byte("{not json"), 0o644); err != nil {
		t.Fatal(err)
	}
	if _, err := LoadLexicon(bad); err == nil {
		t.Error("an unparseable dictionary must be an error")
	}
}

func TestLexiconRoundTripsAFile(t *testing.T) {
	path := filepath.Join(t.TempDir(), "lex.json")
	if err := os.WriteFile(path, []byte(`{"Praxis":"Праксис","  ":"skipped","Nexus":""}`), 0o644); err != nil {
		t.Fatal(err)
	}
	lex, err := LoadLexicon(path)
	if err != nil {
		t.Fatalf("LoadLexicon: %v", err)
	}
	// Blank names and blank spellings are dropped: an entry that says nothing
	// would delete the word it matched.
	if lex.Size() != 1 {
		t.Fatalf("Size = %d, want 1", lex.Size())
	}
	if got := lex.Apply("Praxis молчит"); got != "Праксис молчит" {
		t.Errorf("Apply = %q", got)
	}
}
