package scrabble

import (
	"testing"

	"gitea.iliadenisov.ru/developer/scrabble-solver/board"
	"gitea.iliadenisov.ru/developer/scrabble-solver/internal/encoding"
	"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
)

const plain7 = `.......
.......
.......
...+...
.......
.......
.......`

// plainRules is a 7x7 board with no premiums and English tile values, for isolating
// word-assembly and cross-word logic from premium multipliers.
func plainRules(t *testing.T) *rules.Ruleset {
	t.Helper()
	eng := rules.English()
	rs, err := rules.FromTemplate("plain7", eng.Alphabet, eng.Values, eng.Counts, 2, 7, 50, plain7)
	if err != nil {
		t.Fatal(err)
	}
	return rs
}

// indices: a=0 c=2 o=14 t=19 x=23
func TestEvaluateSimpleWord(t *testing.T) {
	rs := plainRules(t)
	b := board.New(7, 7)
	m, err := Evaluate(b, rs, Horizontal, []Placement{
		{Row: 3, Col: 1, Letter: 2}, {Row: 3, Col: 2, Letter: 0}, {Row: 3, Col: 3, Letter: 19},
	})
	if err != nil {
		t.Fatal(err)
	}
	if m.Main.Score != 5 || m.Score != 5 {
		t.Errorf("cat: main=%d total=%d, want 5/5", m.Main.Score, m.Score)
	}
	if len(m.Cross) != 0 || m.Bonus != 0 {
		t.Errorf("cat: cross=%d bonus=%d, want 0/0", len(m.Cross), m.Bonus)
	}
}

func TestEvaluateCrossWord(t *testing.T) {
	rs := plainRules(t)
	b := board.New(7, 7)
	b.Set(2, 3, encoding.Cell(14, false)) // o
	b.Set(3, 3, encoding.Cell(23, false)) // x

	// Play "at" horizontally on row 4; the 'a' on col 3 forms the cross word "oxa".
	m, err := Evaluate(b, rs, Horizontal, []Placement{
		{Row: 4, Col: 3, Letter: 0}, {Row: 4, Col: 4, Letter: 19},
	})
	if err != nil {
		t.Fatal(err)
	}
	if m.Main.Score != 2 {
		t.Errorf("main 'at' = %d, want 2", m.Main.Score)
	}
	if len(m.Cross) != 1 || m.Cross[0].Score != 10 {
		t.Errorf("cross = %+v, want one word scoring 10 (oxa)", m.Cross)
	}
	if m.Score != 12 {
		t.Errorf("total = %d, want 12", m.Score)
	}
}

func TestEvaluatePremiums(t *testing.T) {
	rs := rules.English()

	// (0,3) is a double-letter square: c(3)*2 + a(1) + t(1) = 8.
	b := board.New(15, 15)
	m, err := Evaluate(b, rs, Horizontal, []Placement{
		{Row: 0, Col: 3, Letter: 2}, {Row: 0, Col: 4, Letter: 0}, {Row: 0, Col: 5, Letter: 19},
	})
	if err != nil {
		t.Fatal(err)
	}
	if m.Score != 8 {
		t.Errorf("DL cat = %d, want 8", m.Score)
	}

	// (1,1) is a double-word square: (c(3) + a(1)) * 2 = 8.
	b2 := board.New(15, 15)
	m2, err := Evaluate(b2, rs, Horizontal, []Placement{
		{Row: 1, Col: 1, Letter: 2}, {Row: 1, Col: 2, Letter: 0},
	})
	if err != nil {
		t.Fatal(err)
	}
	if m2.Score != 8 {
		t.Errorf("DW ca = %d, want 8", m2.Score)
	}
}

func TestEvaluateBingo(t *testing.T) {
	rs := plainRules(t)
	b := board.New(7, 7)
	tiles := make([]Placement, 7)
	for c := range 7 {
		tiles[c] = Placement{Row: 0, Col: c, Letter: 0} // seven a's
	}
	m, err := Evaluate(b, rs, Horizontal, tiles)
	if err != nil {
		t.Fatal(err)
	}
	if m.Bonus != 50 || m.Score != 7+50 {
		t.Errorf("bingo: bonus=%d total=%d, want 50/57", m.Bonus, m.Score)
	}
}

func TestEvaluateErrors(t *testing.T) {
	rs := plainRules(t)
	b := board.New(7, 7)
	b.Set(2, 3, encoding.Cell(14, false))

	if _, err := Evaluate(b, rs, Horizontal, nil); err == nil {
		t.Error("empty play: want error")
	}
	if _, err := Evaluate(b, rs, Horizontal, []Placement{{Row: 2, Col: 3, Letter: 0}}); err == nil {
		t.Error("occupied square: want error")
	}
	if _, err := Evaluate(b, rs, Horizontal, []Placement{
		{Row: 3, Col: 1, Letter: 0}, {Row: 4, Col: 2, Letter: 0},
	}); err == nil {
		t.Error("non-collinear: want error")
	}
	if _, err := Evaluate(b, rs, Horizontal, []Placement{
		{Row: 5, Col: 1, Letter: 0}, {Row: 5, Col: 3, Letter: 0},
	}); err == nil {
		t.Error("gap: want error")
	}
}
