package pokercore

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestAceLowStraight(t *testing.T) {
	hand := Cards{
		AceOfSpades(),
		DeuceOfHearts(),
		ThreeOfDiamonds(),
		FourOfClubs(),
		FiveOfSpades(),
	}
	assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
	ph, err := hand.PokerHand()
	assert.Nil(t, err, "Expected no error")
	assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0")
	assert.Less(t, ph.Score, 100000000000000000, "Expected score to be less than 100000000000000000")
	assert.Equal(t, ph.Score, ScoreStraight+1000*FIVE.Score())
	assert.Equal(t, ph.Description(), "a five high straight")
	assert.True(t, hand.HighestRank() == ACE, "Expected highest rank to be an ace")
	assert.True(t, hand.SortByRankAscending().First().Rank == DEUCE, "Expected first card to be a deuce")
	assert.True(t, hand.SortByRankAscending().Last().Rank == ACE, "Expected last card in sorted to be a ace")
	assert.True(t, hand.SortByRankAscending().Second().Rank == THREE, "Expected second card to be a three")
	assert.True(t, hand.SortByRankAscending().Third().Rank == FOUR, "Expected third card to be a four")
	assert.True(t, hand.SortByRankAscending().Fourth().Rank == FIVE, "Expected fourth card to be a five")
	assert.True(t, hand.SortByRankAscending().Fifth().Rank == ACE, "Expected fifth card to be an ace")
}

func TestAceHighStraight(t *testing.T) {
	hand := Cards{
		TenOfSpades(),
		JackOfHearts(),
		KingOfClubs(),
		AceOfSpades(),
		QueenOfDiamonds(),
	}

	assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
	newDeck := NewDeckFromCards(hand)
	newDeck.ShuffleDeterministically(123456789)
	shuffledHand := newDeck.Deal(5)
	assert.True(t, shuffledHand.containsStraight(), "Expected hand to still be a straight after shuffle")
	assert.True(t, shuffledHand.HighestRank() == ACE, "Expected highest rank to be an ace")
	sortedHand := shuffledHand.SortByRankAscending()
	assert.True(t, sortedHand[0].Rank == TEN, "Expected lowest rank to be a ten")
	assert.True(t, sortedHand[1].Rank == JACK, "Expected second lowest rank to be a jack")
	assert.True(t, sortedHand[2].Rank == QUEEN, "Expected third lowest rank to be a queen")
	assert.True(t, sortedHand[3].Rank == KING, "Expected fourth lowest rank to be a king")
	assert.True(t, sortedHand[4].Rank == ACE, "Expected highest rank to be an ace")
}

func TestOtherStraight(t *testing.T) {
	hand := Cards{
		DeuceOfSpades(),
		ThreeOfHearts(),
		FourOfDiamonds(),
		FiveOfClubs(),
		SixOfSpades(),
	}
	assert.True(t, hand.containsStraight(), "Expected hand to be a straight")

	newDeck := NewDeckFromCards(hand)
	newDeck.ShuffleDeterministically(123456789)
	//fmt.Printf("Shuffled deck: %s\n", newDeck.String())
	//fmt.Printf("new deck has %d cards\n", newDeck.Count())
	shuffledHand := newDeck.Deal(5)
	assert.True(t, shuffledHand.containsStraight(), "Expected hand to still be a straight after shuffle")
	assert.False(t, shuffledHand.containsTwoPair(), "Expected hand to not be two pair")
	assert.False(t, shuffledHand.containsPair(), "Expected hand to not be a pair")
	assert.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to be a six")
	assert.True(t, shuffledHand.SortByRankAscending().First().Rank == DEUCE, "Expected first card to be a deuce")
}

func TestFlush(t *testing.T) {
	hand := Cards{
		AceOfSpades(),
		DeuceOfSpades(),
		ThreeOfSpades(),
		FourOfSpades(),
		SixOfSpades(),
	}
	assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
	newDeck := NewDeckFromCards(hand)
	newDeck.ShuffleDeterministically(123456789)
	//fmt.Printf("Shuffled deck: %s\n", newDeck.String())
	//fmt.Printf("new deck has %d cards\n", newDeck.Count())
	shuffledHand := newDeck.Deal(5)
	assert.True(t, shuffledHand.containsFlush(), "Expected hand to still be a flush after shuffle")

	// flush value is the sum of the ranks, just like high card
	x := ScoreFlush
	x += ACE.Score()
	x += DEUCE.Score()
	x += THREE.Score()
	x += FOUR.Score()
	x += SIX.Score()
	//fmt.Printf("a-2-3-4-6 flush score should be: %d\n", x)

	ph, err := shuffledHand.PokerHand()
	assert.Nil(t, err, "Expected no error")
	assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0")
	assert.Equal(t, ph.Score, x)
}

func TestStraightFlush(t *testing.T) {
	hand := Cards{
		SixOfSpades(),
		DeuceOfSpades(),
		ThreeOfSpades(),
		FourOfSpades(),
		FiveOfSpades(),
	}
	assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
	assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
	assert.True(t, hand.containsStraightFlush(), "Expected hand to be a straight flush")
	assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
	assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
	assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
	assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
	assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
	assert.False(t, hand.containsPair(), "Expected hand to not be a pair")

	assert.True(t, hand.HighestRank() == SIX, "Expected highest rank to be a six")

	nd := NewDeckFromCards(hand)
	nd.ShuffleDeterministically(123456789)
	//fmt.Printf("Shuffled deck: %s\n", nd.String())
	//fmt.Printf("new deck has %d cards\n", nd.Count())
	shuffledHand := nd.Deal(5)
	assert.True(t, shuffledHand.containsStraightFlush(), "Expected hand to still be a straight flush after shuffle")
	assert.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to still be a six after shuffle")
	assert.True(t, shuffledHand.HighestRank() == SIX, "Expected highest rank to be a six after shuffle even with aces low")
}

func TestRoyalFlush(t *testing.T) {
	hand := Cards{
		TenOfSpades(),
		JackOfSpades(),
		QueenOfSpades(),
		KingOfSpades(),
		AceOfSpades(),
	}
	assert.True(t, hand.containsStraight(), "Expected hand to be a straight")
	assert.True(t, hand.containsFlush(), "Expected hand to be a flush")
	assert.True(t, hand.containsStraightFlush(), "Expected hand to be a straight flush")
	assert.True(t, hand.containsRoyalFlush(), "Expected hand to be a royal flush")

	assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
	assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
	assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
	assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
	assert.False(t, hand.containsPair(), "Expected hand to not be a pair")

	assert.True(t, hand.HighestRank() == ACE, "Expected highest rank to be an ace")
	assert.False(t, hand.HighestRank() == TEN, "Expected highest rank to not be an ace")
}

func TestUnmadeHand(t *testing.T) {
	hand := Cards{
		TenOfSpades(),
		JackOfDiamonds(),
		QueenOfSpades(),
		KingOfSpades(),
		DeuceOfSpades(),
	}
	assert.False(t, hand.containsStraight(), "Expected hand to not be a straight")
	assert.False(t, hand.containsFlush(), "Expected hand to not be a flush")
	assert.False(t, hand.containsStraightFlush(), "Expected hand to not be a straight flush")
	assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
	assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
	assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
	assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
	assert.False(t, hand.containsTwoPair(), "Expected hand to not be two pair")
	assert.False(t, hand.containsPair(), "Expected hand to not be a pair")
	assert.True(t, hand.HighestRank() == KING, "Expected highest rank to be a king")
	assert.True(t, hand.isUnmadeHand(), "Expected hand to be unmade")
}

func TestTwoPair(t *testing.T) {
	hand := Cards{
		KingOfSpades(),
		JackOfDiamonds(),
		JackOfSpades(),
		KingOfDiamonds(),
		TenOfSpades(),
	}
	assert.False(t, hand.containsStraight(), "Expected hand to not be a straight")
	assert.False(t, hand.containsFlush(), "Expected hand to not be a flush")
	assert.False(t, hand.containsStraightFlush(), "Expected hand to not be a straight flush")
	assert.False(t, hand.containsRoyalFlush(), "Expected hand to not be a royal flush")
	assert.False(t, hand.containsFourOfAKind(), "Expected hand to not be four of a kind")
	assert.False(t, hand.containsFullHouse(), "Expected hand to not be a full house")
	assert.False(t, hand.containsThreeOfAKind(), "Expected hand to not be three of a kind")
	assert.True(t, hand.containsTwoPair(), "Expected hand to be two pair")
	assert.True(t, hand.containsPair(), "Expected hand to also be a pair")
	assert.True(t, hand.HighestRank() == KING, "Expected highest rank to be a king")
	assert.False(t, hand.isUnmadeHand(), "Expected hand to not be unmade")
}

func TestHandScore(t *testing.T) {
	hand := Cards{
		KingOfSpades(),
		JackOfDiamonds(),
		JackOfSpades(),
		KingOfDiamonds(),
		TenOfSpades(),
	}

	ph, error := hand.PokerHand()
	assert.Nil(t, error, "Expected no error")
	assert.True(t, ph.Score > 0, "Expected score to be nonzero 0")
	assert.True(t, ph.Score < 100000000000000000, "Expected score to be less than 100000000000000000")

	//fmt.Printf("PokerHand: %v+\n", ph)
	//fmt.Printf("PH score: %d\n", ph.Score)
}

func TestTwoPairBug(t *testing.T) {
	// this is an actual bug in the first implementation
	c, err := NewCardsFromString("9♠,9♣,Q♥,Q♦,K♣")
	assert.Nil(t, err, "Expected no error")

	//fmt.Printf("Cards: %v+\n", c)

	ph, err := c.PokerHand()
	assert.Nil(t, err, "Expected no error")
	assert.Greater(t, ph.Score, 0, "Expected score to be nonzero 0")
	desc := ph.Description()
	assert.Equal(t, desc, "two pair, queens and nines with a king")

	//fmt.Printf("PokerHand: %v+\n", ph)
	//fmt.Printf("PH score: %d\n", ph.Score)
}
