package main

import (
	"math/rand"
	"testing"
)

func TestPokerHandStrengthPocketAcesBeatsRandomMost(t *testing.T) {
	rnd := rand.New(rand.NewSource(1))
	aces := [2]Card{c(Ace, Spades), c(Ace, Hearts)}
	strength := pokerHandStrength(aces, nil, 1, 300, rnd)
	if strength < 0.7 {
		t.Errorf("пара тузов против одного случайного соперника должна выигрывать заметно чаще, чем в 70%% случаев, получено %.2f", strength)
	}
}

func TestPokerHandStrengthWeakHandLowerThanPocketAces(t *testing.T) {
	rnd := rand.New(rand.NewSource(1))
	aces := [2]Card{c(Ace, Spades), c(Ace, Hearts)}
	weak := [2]Card{c(Two, Clubs), c(Seven, Diamonds)}
	acesStrength := pokerHandStrength(aces, nil, 1, 300, rnd)
	weakStrength := pokerHandStrength(weak, nil, 1, 300, rnd)
	if weakStrength >= acesStrength {
		t.Errorf("слабая рука 2-7 разномастные не должна оцениваться сильнее пары тузов: слабая=%.2f тузы=%.2f", weakStrength, acesStrength)
	}
}

func TestCrudeStrengthPairHigherThanHighCardOnly(t *testing.T) {
	pair := crudeStrength([2]Card{c(King, Clubs), c(King, Diamonds)}, nil)
	highCard := crudeStrength([2]Card{c(Two, Clubs), c(Seven, Diamonds)}, nil)
	if pair <= highCard {
		t.Errorf("пара королей должна оцениваться выше, чем непарные 2-7, получено пара=%.2f старшая=%.2f", pair, highCard)
	}
}

func TestRemainingDeckExcludesKnownCards(t *testing.T) {
	hole := [2]Card{c(Ace, Spades), c(King, Hearts)}
	board := []Card{c(Two, Clubs), c(Three, Diamonds), c(Four, Hearts)}
	deck := remainingDeck(hole, board)
	if len(deck) != 52-5 {
		t.Fatalf("ожидалось 47 оставшихся карт, получено %d", len(deck))
	}
	known := map[Card]bool{hole[0]: true, hole[1]: true}
	for _, cc := range board {
		known[cc] = true
	}
	for _, card := range deck {
		if known[card] {
			t.Errorf("известная карта %v не должна встречаться в оставшейся колоде", card)
		}
	}
}

func playPokerFullTournament(t *testing.T, g *PokerGameState, bots []PokerBot, rnd *rand.Rand, maxHands int) {
	t.Helper()
	for hand := 0; hand < maxHands && g.Phase != PokerPhaseTournamentOver; hand++ {
		steps := 0
		for g.Phase != PokerPhaseShowdown && g.Phase != PokerPhaseTournamentOver && steps < 500 {
			steps++
			seat := g.CurrentPlayerIdx
			if err := bots[seat].PlayFullTurn(g, seat, rnd); err != nil {
				t.Fatalf("раздача %d, шаг %d: неожиданная ошибка бота на месте %d: %v", hand, steps, seat, err)
			}
		}
		if g.Phase != PokerPhaseShowdown && g.Phase != PokerPhaseTournamentOver {
			t.Fatalf("раздача %d не завершилась за %d шагов — похоже на зависание", hand, steps)
		}
		if g.Phase == PokerPhaseShowdown {
			g.NextHand(rnd)
		}
	}
	if g.Phase != PokerPhaseTournamentOver {
		t.Fatalf("турнир не завершился за %d раздач", maxHands)
	}
}

func TestPokerBotFullTournamentAllEasy(t *testing.T) {
	rnd := rand.New(rand.NewSource(1))
	for trial := 0; trial < 3; trial++ {
		g := NewPokerGame(4, 500, 10, 20, rnd)
		bots := []PokerBot{{Difficulty: PokerDifficultyEasy}, {Difficulty: PokerDifficultyEasy}, {Difficulty: PokerDifficultyEasy}, {Difficulty: PokerDifficultyEasy}}
		playPokerFullTournament(t, g, bots, rnd, 300)
	}
}

func TestPokerBotFullTournamentMixedDifficulties(t *testing.T) {
	rnd := rand.New(rand.NewSource(2))
	for trial := 0; trial < 2; trial++ {
		g := NewPokerGame(4, 500, 10, 20, rnd)
		bots := []PokerBot{
			{Difficulty: PokerDifficultyEasy},
			{Difficulty: PokerDifficultyMedium},
			{Difficulty: PokerDifficultyHard},
			{Difficulty: PokerDifficultyMedium},
		}
		playPokerFullTournament(t, g, bots, rnd, 300)
	}
}

func TestPokerBotFullTournamentHeadsUp(t *testing.T) {
	rnd := rand.New(rand.NewSource(3))
	for trial := 0; trial < 3; trial++ {
		g := NewPokerGame(2, 500, 10, 20, rnd)
		bots := []PokerBot{{Difficulty: PokerDifficultyHard}, {Difficulty: PokerDifficultyEasy}}
		playPokerFullTournament(t, g, bots, rnd, 300)
	}
}

func TestPokerBotFullTournamentSixPlayers(t *testing.T) {
	rnd := rand.New(rand.NewSource(4))
	g := NewPokerGame(6, 500, 10, 20, rnd)
	bots := make([]PokerBot, 6)
	for i := range bots {
		bots[i] = PokerBot{Difficulty: PokerDifficulty(i % 3)}
	}
	playPokerFullTournament(t, g, bots, rnd, 500)
}

func TestPokerBotNeverActsWithInvalidAmount(t *testing.T) {
	rnd := rand.New(rand.NewSource(5))
	for trial := 0; trial < 5; trial++ {
		g := NewPokerGame(3, 200, 10, 20, rnd)
		bots := []PokerBot{{Difficulty: PokerDifficultyHard}, {Difficulty: PokerDifficultyHard}, {Difficulty: PokerDifficultyHard}}
		steps := 0
		for g.Phase != PokerPhaseShowdown && g.Phase != PokerPhaseTournamentOver && steps < 200 {
			steps++
			seat := g.CurrentPlayerIdx
			if err := bots[seat].PlayFullTurn(g, seat, rnd); err != nil {
				t.Fatalf("бот попытался сделать недопустимое действие: %v", err)
			}
		}
	}
}
