package main

import "testing"

func TestTicTacToeModelPlaceMarkViaDigitKey(t *testing.T) {
	m := NewTicTacToeModel(TicTacToeDifficultyEasy)
	m.game = NewTicTacToeGame(TicTacToeX) // фиксируем: человек играет крестиками, сейчас его ход

	next, _ := m.Update(key("5"))
	m = next.(TicTacToeModel)
	if m.game.Board[4] != TicTacToeX {
		t.Fatalf("ожидался крестик в клетке 5 (индекс 4), доска: %v", m.game.Board)
	}
	if m.isError {
		t.Errorf("неожиданная ошибка: %s", m.message)
	}
}

func TestTicTacToeModelCursorMovementAndEnter(t *testing.T) {
	m := NewTicTacToeModel(TicTacToeDifficultyEasy)
	m.game = NewTicTacToeGame(TicTacToeX)
	m.cursor = 4 // центр

	next, _ := m.Update(key("up"))
	m = next.(TicTacToeModel)
	if m.cursor != 1 {
		t.Fatalf("ожидался курсор на клетке 1 (индекс 1) после 'up' из центра, получено %d", m.cursor)
	}

	next, _ = m.Update(key("enter"))
	m = next.(TicTacToeModel)
	if m.game.Board[1] != TicTacToeX {
		t.Errorf("ожидался крестик в клетке под курсором (индекс 1), доска: %v", m.game.Board)
	}
}

func TestTicTacToeModelRejectsMoveWhenNotHumanTurn(t *testing.T) {
	m := NewTicTacToeModel(TicTacToeDifficultyEasy)
	m.game = NewTicTacToeGame(TicTacToeO) // человек — нолики, сейчас ход бота (крестиков)

	next, _ := m.Update(key("5"))
	m = next.(TicTacToeModel)
	if m.game.MovesPlayed != 0 {
		t.Errorf("ход не должен был приняться — сейчас очередь бота, а не человека")
	}
}

func TestTicTacToeModelBotMovesFirstWhenHumanIsO(t *testing.T) {
	m := NewTicTacToeModel(TicTacToeDifficultyHard)
	m.game = NewTicTacToeGame(TicTacToeO)

	next, _ := m.Update(botMoveMsg{})
	m = next.(TicTacToeModel)
	if m.game.MovesPlayed != 1 {
		t.Fatalf("бот должен был сходить первым, MovesPlayed=%d", m.game.MovesPlayed)
	}
	if m.game.CurrentTurn != TicTacToeO {
		t.Errorf("после хода бота должна была наступить очередь человека (ноликов)")
	}
}

func TestTicTacToeModelRejectsOccupiedCellWithError(t *testing.T) {
	m := NewTicTacToeModel(TicTacToeDifficultyEasy)
	m.game = NewTicTacToeGame(TicTacToeX)
	m.game.Board[0] = TicTacToeO
	m.game.Board[8] = TicTacToeX // клетки заняты вручную, ход всё ещё X

	next, _ := m.Update(key("1"))
	m = next.(TicTacToeModel)
	if !m.isError {
		t.Errorf("ожидалась ошибка при попытке сходить в занятую клетку")
	}
}

func TestTicTacToeModelNewGameAfterOver(t *testing.T) {
	m := NewTicTacToeModel(TicTacToeDifficultyEasy)
	m.game = NewTicTacToeGame(TicTacToeX)
	m.game.Board = [9]TicTacToeMark{
		TicTacToeX, TicTacToeX, TicTacToeX,
		TicTacToeO, TicTacToeO, TicTacToeEmpty,
		TicTacToeEmpty, TicTacToeEmpty, TicTacToeEmpty,
	}
	m.game.Phase = TicTacToePhaseOver
	m.game.Result = &TicTacToeResult{Winner: TicTacToeX, Line: [3]int{0, 1, 2}}

	next, _ := m.Update(key("n"))
	next2 := next.(TicTacToeModel)
	if next2.game.Phase != TicTacToePhasePlay {
		t.Errorf("после 'n' должна была начаться новая партия")
	}
	if next2.game == m.game {
		t.Errorf("новая партия должна была получить новое состояние, а не переиспользовать старое")
	}
}

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

func TestTicTacToeModelActionsIgnoredAfterGameOver(t *testing.T) {
	m := NewTicTacToeModel(TicTacToeDifficultyEasy)
	m.game = NewTicTacToeGame(TicTacToeX)
	m.game.Phase = TicTacToePhaseOver
	m.game.Result = &TicTacToeResult{Draw: true}

	next, _ := m.Update(key("5"))
	m = next.(TicTacToeModel)
	if m.game.MovesPlayed != 0 {
		t.Errorf("ход не должен был приниматься после завершения партии")
	}
}
