package main

import (
	"fmt"
	"testing"
)

func TestSenetModelRollViaKey(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceA)
	m.game = NewSenetGame(SenetPlayerA)
	m.humanColor = SenetPlayerA

	next, _ := m.Update(key("r"))
	m = next.(SenetModel)
	if m.game.DiceValue == 0 {
		t.Errorf("после броска значение кости не должно оставаться нулевым")
	}
}

func TestSenetModelApplyMoveViaDigitKey(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceA)
	m.game = NewSenetGame(SenetPlayerA)
	m.humanColor = SenetPlayerA
	m.game.Roll(2)
	if m.game.CurrentPlayer != SenetPlayerA {
		t.Fatalf("тест предполагает ход человека")
	}
	moves := m.game.LegalMoves()
	if len(moves) == 0 {
		t.Fatalf("ожидался хотя бы один легальный ход")
	}

	next, _ := m.Update(key("1"))
	m = next.(SenetModel)
	if m.isError {
		t.Fatalf("неожиданная ошибка: %s", m.message)
	}
}

func TestSenetModelRejectsMoveKeyBeforeRoll(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceA)
	m.game = NewSenetGame(SenetPlayerA)
	m.humanColor = SenetPlayerA

	next, _ := m.Update(key("1"))
	m = next.(SenetModel)
	if m.game.BorneOff[SenetPlayerA] != 0 || m.game.Rolled {
		t.Errorf("ход не должен был приняться до броска костей")
	}
}

func TestSenetModelBotPlaysWhenNotHumanTurn(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceA)
	m.game = NewSenetGame(SenetPlayerB)
	m.humanColor = SenetPlayerA

	next, _ := m.Update(botMoveMsg{})
	m = next.(SenetModel)
	if m.game.CurrentPlayer != SenetPlayerA && m.game.Result == nil {
		t.Errorf("после хода бота (без доп.бросков) очередь должна была перейти к человеку")
	}
}

func TestSenetModelIgnoresBotMoveMsgOnHumanTurn(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceA)
	m.game = NewSenetGame(SenetPlayerA)
	m.humanColor = SenetPlayerA

	next, _ := m.Update(botMoveMsg{})
	m = next.(SenetModel)
	if m.game.Rolled {
		t.Errorf("botMoveMsg не должно было ничего менять, пока сейчас ход человека")
	}
}

func TestSenetModelQReturnsToMenuNotQuitsApp(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceA)
	next, _ := m.Update(key("q"))
	m = next.(SenetModel)
	if !m.backToMenu {
		t.Errorf("ожидался флаг backToMenu после 'q'")
	}
	if m.quitting {
		t.Errorf("'q' не должно завершать приложение целиком")
	}
}

func TestSenetModelActionsIgnoredAfterGameOver(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceA)
	m.game = NewSenetGame(SenetPlayerA)
	m.humanColor = SenetPlayerA
	m.game.Result = &SenetResult{Winner: SenetPlayerA}

	next, _ := m.Update(key("r"))
	m = next.(SenetModel)
	if m.game.Rolled {
		t.Errorf("действия после завершения партии не должны приниматься")
	}
}

func TestSenetModelFullAutoPlaythrough(t *testing.T) {
	m := NewSenetModel(SenetDifficultyMedium, ColorChoiceA)
	m.game = NewSenetGame(SenetPlayerA)
	m.humanColor = SenetPlayerA
	humanBot := SenetBot{Difficulty: SenetDifficultyMedium}

	steps := 0
	for m.game.Result == nil && steps < 30000 {
		steps++
		if m.game.CurrentPlayer != m.humanColor {
			next, _ := m.Update(botMoveMsg{})
			m = next.(SenetModel)
			continue
		}
		if !m.game.Rolled {
			next, _ := m.Update(key("r"))
			m = next.(SenetModel)
			continue
		}
		moves := m.game.LegalMoves()
		if len(moves) == 0 {
			t.Fatalf("LegalMoves пуст при Rolled=true для хода человека — не должно происходить")
		}
		if len(moves) > 9 {
			t.Fatalf("число легальных ходов превысило 9: %d", len(moves))
		}
		chosen := humanBot.DecideMove(m.game, m.rng)
		idx := -1
		for i, mv := range moves {
			if mv.From == chosen {
				idx = i
				break
			}
		}
		if idx < 0 {
			idx = 0
		}
		next, _ := m.Update(key(fmt.Sprintf("%d", idx+1)))
		m = next.(SenetModel)
		if m.isError {
			t.Fatalf("неожиданная ошибка при ходе: %s", m.message)
		}
	}
	if m.game.Result == nil {
		t.Fatalf("партия не завершилась за %d шагов", steps)
	}
}

func TestNewSenetModelRespectsColorChoiceA(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceA)
	if m.humanColor != SenetPlayerA {
		t.Errorf("при выборе ColorChoiceA человек должен был играть за SenetPlayerA, получено %v", m.humanColor)
	}
}

func TestNewSenetModelRespectsColorChoiceB(t *testing.T) {
	m := NewSenetModel(SenetDifficultyEasy, ColorChoiceB)
	if m.humanColor != SenetPlayerB {
		t.Errorf("при выборе ColorChoiceB человек должен был играть за SenetPlayerB, получено %v", m.humanColor)
	}
}

func TestNewSenetModelRandomColorGivesValidColor(t *testing.T) {
	seenA, seenB := false, false
	for i := 0; i < 50 && !(seenA && seenB); i++ {
		m := NewSenetModel(SenetDifficultyEasy, ColorChoiceRandom)
		if m.humanColor == SenetPlayerA {
			seenA = true
		} else {
			seenB = true
		}
	}
	if !seenA || !seenB {
		t.Errorf("случайный выбор цвета должен был давать оба варианта за 50 попыток: A=%v B=%v", seenA, seenB)
	}
}
