package scrabble

import (
	"testing"

	"github.com/iliadenisov/alphabet"

	"gitea.iliadenisov.ru/developer/scrabble-solver/board"
	"gitea.iliadenisov.ru/developer/scrabble-solver/dictdawg"
	"gitea.iliadenisov.ru/developer/scrabble-solver/wordlist"
)

func newTestSolver(t *testing.T) *Solver {
	t.Helper()
	words := wordlist.Encode(testWords, alphabet.Latin(), 2, 15)
	f, err := dictdawg.Build(alphabet.Latin(), words)
	if err != nil {
		t.Fatal(err)
	}
	return NewSolver(plainRulesShared, f)
}

func TestSolverGenerateMovesRanked(t *testing.T) {
	s := newTestSolver(t)
	b := board.New(s.rules.Rows, s.rules.Cols)
	moves := s.GenerateMoves(b, makeRack("cat", 0), Both)
	if len(moves) == 0 {
		t.Fatal("no first moves generated")
	}
	for i := 1; i < len(moves); i++ {
		if moves[i-1].Score < moves[i].Score {
			t.Fatalf("moves not ranked: %d before %d", moves[i-1].Score, moves[i].Score)
		}
	}
}

func TestSolverValidatePlay(t *testing.T) {
	s := newTestSolver(t)
	// indices: c=2 a=0 t=19 z=25
	cat := []Placement{{Row: 3, Col: 2, Letter: 2}, {Row: 3, Col: 3, Letter: 0}, {Row: 3, Col: 4, Letter: 19}}

	// First move through the centre (3,3) is legal.
	if _, err := s.ValidatePlay(board.New(s.rules.Rows, s.rules.Cols), Horizontal, cat); err != nil {
		t.Errorf("valid first move rejected: %v", err)
	}

	// First move that misses the centre is rejected.
	off := []Placement{{Row: 0, Col: 0, Letter: 2}, {Row: 0, Col: 1, Letter: 0}, {Row: 0, Col: 2, Letter: 19}}
	if _, err := s.ValidatePlay(board.New(s.rules.Rows, s.rules.Cols), Horizontal, off); err == nil {
		t.Error("first move off the centre was accepted")
	}

	// A non-word ("caz") is rejected.
	caz := []Placement{{Row: 3, Col: 2, Letter: 2}, {Row: 3, Col: 3, Letter: 0}, {Row: 3, Col: 4, Letter: 25}}
	if _, err := s.ValidatePlay(board.New(s.rules.Rows, s.rules.Cols), Horizontal, caz); err == nil {
		t.Error("non-word 'caz' was accepted")
	}

	// A disconnected play on a non-empty board is rejected.
	b := board.New(s.rules.Rows, s.rules.Cols)
	placeWord(b, 3, 2, Horizontal, "cat")
	disc := []Placement{{Row: 0, Col: 0, Letter: 0}, {Row: 0, Col: 1, Letter: 18}} // "as" far away
	if _, err := s.ValidatePlay(b, Horizontal, disc); err == nil {
		t.Error("disconnected play was accepted")
	}

	// Extending "cat" to "cats" connects and is a word.
	cats := []Placement{{Row: 3, Col: 5, Letter: 18}} // s after cat
	if m, err := s.ValidatePlay(b, Horizontal, cats); err != nil {
		t.Errorf("valid extension rejected: %v", err)
	} else if string(m.Main.Letters) != string([]byte{2, 0, 19, 18}) {
		t.Errorf("main word = %v, want cats", m.Main.Letters)
	}
}

func TestSolverScorePlay(t *testing.T) {
	s := newTestSolver(t)
	b := board.New(s.rules.Rows, s.rules.Cols)
	m, err := s.ScorePlay(b, Horizontal, []Placement{
		{Row: 3, Col: 2, Letter: 2}, {Row: 3, Col: 3, Letter: 0}, {Row: 3, Col: 4, Letter: 19},
	})
	if err != nil {
		t.Fatal(err)
	}
	if m.Score != 5 { // c3 a1 t1, no premiums on the plain board
		t.Errorf("cat score = %d, want 5", m.Score)
	}
}
