package main

import "testing"

func TestThousandModelDealsThreePlayers(t *testing.T) {
	m := NewThousandModel()
	if len(m.game.Players) != 3 {
		t.Errorf("ожидалось 3 игрока, получено %d", len(m.game.Players))
	}
}

func TestThousandModelBidViaKeys(t *testing.T) {
	m := NewThousandModel()
	m.game.CurrentBidderIdx = m.humanIdx
	m.bidAmount = 100

	next, _ := m.Update(key("enter"))
	m = next.(ThousandModel)
	if m.isError {
		t.Fatalf("неожиданная ошибка: %s", m.message)
	}
	if m.game.HighBid != 100 {
		t.Fatalf("ожидалась ставка 100, получено %d", m.game.HighBid)
	}
}

func TestThousandModelDeclareBlindViaKey(t *testing.T) {
	m := NewThousandModel()
	m.game.CurrentBidderIdx = m.humanIdx
	m.game.firstBidderIdx = m.humanIdx
	m.game.bidsMade = 0

	next, _ := m.Update(key("v"))
	m = next.(ThousandModel)
	if m.isError {
		t.Fatalf("неожиданная ошибка: %s", m.message)
	}
	if !m.game.PlayedBlind {
		t.Fatalf("ожидалось, что раздача разыгрывается втёмную")
	}
	if m.game.DeclarerIdx != m.humanIdx {
		t.Errorf("человек должен был стать declarer'ом")
	}
	if m.game.FinalBid != 120 {
		t.Errorf("ожидалась фиксированная заявка 120, получено %d", m.game.FinalBid)
	}
}

func TestThousandModelPassRejectedForFirstBidder(t *testing.T) {
	m := NewThousandModel()
	m.game.CurrentBidderIdx = m.humanIdx
	m.game.firstBidderIdx = m.humanIdx
	m.game.bidsMade = 0

	next, _ := m.Update(key("p"))
	m = next.(ThousandModel)
	if !m.isError {
		t.Fatalf("первый ходящий не может пасовать, ошибка ожидалась")
	}
}

func TestThousandModelDiscardViaKeys(t *testing.T) {
	m := NewThousandModel()
	m.game.CurrentBidderIdx = m.humanIdx
	m.bidAmount = 100
	next, _ := m.Update(key("enter"))
	m = next.(ThousandModel)
	if m.isError {
		t.Fatalf("неожиданная ошибка ставки: %s", m.message)
	}
	if err := m.game.Pass(); err != nil {
		t.Fatalf("ошибка подготовки: %v", err)
	}
	if err := m.game.Pass(); err != nil {
		t.Fatalf("ошибка подготовки: %v", err)
	}
	if m.game.Phase != ThousandPhaseDiscard {
		t.Fatalf("ожидалась фаза снoса")
	}
	if m.game.DeclarerIdx != m.humanIdx {
		t.Fatalf("человек должен был стать declarer")
	}
	m.cursor = 0

	next, _ = m.Update(key("enter"))
	m = next.(ThousandModel)
	if m.isError {
		t.Fatalf("неожиданная ошибка при первом сносе: %s", m.message)
	}
	if len(m.game.Players[m.humanIdx].Hand) != 9 {
		t.Fatalf("после первого сноса должно остаться 9 карт, получено %d", len(m.game.Players[m.humanIdx].Hand))
	}
}

func TestThousandModelQReturnsToMenu(t *testing.T) {
	m := NewThousandModel()
	next, _ := m.Update(key("q"))
	m = next.(ThousandModel)
	if !m.backToMenu {
		t.Fatalf("ожидался флаг backToMenu после 'q'")
	}
	if m.quitting {
		t.Fatalf("'q' не должен закрывать всё приложение")
	}
}

// TestThousandModelFullAutoPlaythrough прогоняет TUI-модель от
// начала до конца нескольких раздач, где "человек" отыгрывает свои
// решения через реальные нажатия клавиш (используя ту же логику,
// что и бот).
func TestThousandModelFullAutoPlaythrough(t *testing.T) {
	m := NewThousandModel()
	bot := ThousandBot{}

	const maxHands = 30
	for hand := 0; hand < maxHands; hand++ {
		const maxSteps = 3000
		steps := 0
		for m.game.Phase != ThousandPhaseHandOver && steps < maxSteps {
			steps++
			if m.actorForPhase() != m.humanIdx {
				next, _ := m.Update(botMoveMsg{})
				m = next.(ThousandModel)
				continue
			}

			switch m.game.Phase {
			case ThousandPhaseBidding:
				if amount, ok := bot.DecideBid(m.game); ok {
					m.bidAmount = amount
					next, _ := m.Update(key("enter"))
					m = next.(ThousandModel)
				} else {
					next, _ := m.Update(key("p"))
					m = next.(ThousandModel)
				}
			case ThousandPhaseDiscard:
				discards := bot.DecideDiscards(m.game.Players[m.humanIdx].Hand)
				for _, card := range discards {
					for i, hc := range m.currentHand() {
						if hc == card {
							m.cursor = i
							break
						}
					}
					next, _ := m.Update(key("enter"))
					m = next.(ThousandModel)
				}
			case ThousandPhaseTrick:
				card := bot.DecidePlay(m.game)
				for i, hc := range m.currentHand() {
					if hc == card {
						m.cursor = i
						break
					}
				}
				next, _ := m.Update(key("enter"))
				m = next.(ThousandModel)
			}
		}
		if steps >= maxSteps {
			t.Fatalf("раздача %d: не завершилась через TUI за %d шагов (фаза %v)", hand, maxSteps, m.game.Phase)
		}
		if m.game.Result == nil {
			t.Fatalf("раздача %d: завершилась без результата", hand)
		}
		if m.game.Result.MatchOver {
			return
		}
		next, _ := m.Update(key("n"))
		m = next.(ThousandModel)
	}
}
