package main

import "testing"

func TestNewTicTacToeGameXAlwaysStartsFirst(t *testing.T) {
	g := NewTicTacToeGame(TicTacToeO)
	if g.CurrentTurn != TicTacToeX {
		t.Errorf("крестики должны ходить первыми независимо от того, за кого играет человек")
	}
}

func TestNewTicTacToeGameDefaultsToXOnInvalidMark(t *testing.T) {
	g := NewTicTacToeGame(TicTacToeEmpty)
	if g.HumanMark != TicTacToeX {
		t.Errorf("недопустимый знак человека должен откатываться на крестики по умолчанию")
	}
}

func TestPlaceMarkRejectsOccupiedCell(t *testing.T) {
	g := NewTicTacToeGame(TicTacToeX)
	if err := g.PlaceMark(0); err != nil {
		t.Fatalf("неожиданная ошибка: %v", err)
	}
	if err := g.PlaceMark(0); err != ErrTicTacToeCellOccupied {
		t.Errorf("ожидалась ошибка занятой клетки, получено: %v", err)
	}
}

func TestPlaceMarkRejectsOutOfRange(t *testing.T) {
	g := NewTicTacToeGame(TicTacToeX)
	if err := g.PlaceMark(-1); err != ErrTicTacToeInvalidPosition {
		t.Errorf("ожидалась ошибка недопустимой позиции для -1, получено: %v", err)
	}
	if err := g.PlaceMark(9); err != ErrTicTacToeInvalidPosition {
		t.Errorf("ожидалась ошибка недопустимой позиции для 9, получено: %v", err)
	}
}

func TestPlaceMarkRejectsAfterGameOver(t *testing.T) {
	g := NewTicTacToeGame(TicTacToeX)
	// X быстро выигрывает верхнюю строку: 0,1,2
	moves := []int{0, 3, 1, 4, 2} // X: 0,1,2  O: 3,4
	for _, pos := range moves {
		if err := g.PlaceMark(pos); err != nil {
			t.Fatalf("неожиданная ошибка на ходу %d: %v", pos, err)
		}
	}
	if g.Phase != TicTacToePhaseOver || g.Result.Winner != TicTacToeX {
		t.Fatalf("ожидалась победа крестиков, получено Phase=%v Result=%+v", g.Phase, g.Result)
	}
	if err := g.PlaceMark(5); err != ErrTicTacToeGameOver {
		t.Errorf("ожидалась ошибка завершённой игры, получено: %v", err)
	}
}

func TestAllEightWinLinesDetected(t *testing.T) {
	lines := [8][3]int{
		{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
		{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
		{0, 4, 8}, {2, 4, 6},
	}
	for _, line := range lines {
		var board [9]TicTacToeMark
		board[line[0]] = TicTacToeX
		board[line[1]] = TicTacToeX
		board[line[2]] = TicTacToeX
		winner, gotLine, ok := ticTacToeCheckWin(board)
		if !ok || winner != TicTacToeX {
			t.Errorf("линия %v должна была засчитаться как победная", line)
		}
		if gotLine != line {
			t.Errorf("ожидалась выигрышная линия %v, получено %v", line, gotLine)
		}
	}
}

func TestDrawWhenBoardFullWithoutWinner(t *testing.T) {
	g := NewTicTacToeGame(TicTacToeX)
	// классическая ничейная раскладка:
	//   X O X
	//   X O O
	//   O X X
	moves := []int{0, 1, 2, 4, 3, 5, 7, 6, 8}
	for i, pos := range moves {
		if err := g.PlaceMark(pos); err != nil {
			t.Fatalf("ход %d (клетка %d): неожиданная ошибка %v", i, pos, err)
		}
		if g.Phase == TicTacToePhaseOver && i != len(moves)-1 {
			t.Fatalf("партия завершилась преждевременно на ходу %d (клетка %d), доска: %v", i, pos, g.Board)
		}
	}
	if g.Phase != TicTacToePhaseOver || !g.Result.Draw {
		t.Fatalf("ожидалась ничья, получено Phase=%v Result=%+v", g.Phase, g.Result)
	}
}

func TestTurnAlternatesBetweenMoves(t *testing.T) {
	g := NewTicTacToeGame(TicTacToeX)
	if g.CurrentTurn != TicTacToeX {
		t.Fatalf("ожидался первый ход крестиков")
	}
	g.PlaceMark(0)
	if g.CurrentTurn != TicTacToeO {
		t.Errorf("после хода крестиков должна ходить нолики")
	}
	g.PlaceMark(1)
	if g.CurrentTurn != TicTacToeX {
		t.Errorf("после хода ноликов должны снова ходить крестики")
	}
}
