import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

public class BlackjackCompleteTester
{
    private static final double MONEY_TOLERANCE = 0.009;

    @Test
    public void testGetPlayersMoney()
    {
        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {8, BlackjackCard.KING_VALUE, BlackjackCard.ACE_VALUE, 10});

        Blackjack bj = new Blackjack(1000);

        assertEquals(1000, bj.getPlayersMoney(), MONEY_TOLERANCE);
    }

    @Test
    public void testPlaceBetAndDealCards()
    {
        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {8, BlackjackCard.KING_VALUE, BlackjackCard.ACE_VALUE, 10});

        Blackjack bj = new Blackjack(1000);

        bj.placeBetAndDealCards(10);
        
        assertEquals(990, bj.getPlayersMoney(), MONEY_TOLERANCE);
        assertEquals(10, bj.getPlayersBet(), MONEY_TOLERANCE);

        int[] expectedPlayersHandCardValues = {8, BlackjackCard.ACE_VALUE};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedPlayersHandCardValues, bj.getPlayersHand().getCards()));

        int[] expectedDealersHandCardValues = {BlackjackCard.KING_VALUE, 10};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));
    }

    @Test
    public void testCanHit()
    {
        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {BlackjackCard.ACE_VALUE, BlackjackCard.KING_VALUE, 10, BlackjackCard.ACE_VALUE});

        Blackjack bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        
        assertTrue( ! bj.canHit() );


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {9, BlackjackCard.ACE_VALUE, 10, BlackjackCard.QUEEN_VALUE});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        assertTrue( ! bj.canHit() );


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{BlackjackCard.ACE_VALUE, 8, 10, 2});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        assertTrue( ! bj.canHit() );


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{5, 7, 10, 2});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        assertTrue(bj.canHit());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{5, 7, 10, 3, 6});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.hit();

        assertTrue( ! bj.canHit() );


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{5, 7, 10, 3, 3});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.hit();

        assertTrue(bj.canHit());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{5, 6, 10, 2, 10});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.hit();

        assertTrue( ! bj.canHit() );
    }

    @Test
    public void testPlayDealersHand()
    {
        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, BlackjackCard.ACE_VALUE, 10, BlackjackCard.KING_VALUE});

        Blackjack bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        int[] expectedDealersHandCardValues = {BlackjackCard.ACE_VALUE, BlackjackCard.KING_VALUE};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 8, 10, 9});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]{8, 9};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 8, 10, 10});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]{8, 10};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 8, 10, 2, 7});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]{8, 2, 7};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 8, 10, 2, 6, 2});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]{8, 2, 6, 2};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {8, BlackjackCard.KING_VALUE, 10, 6, BlackjackCard.JACK_VALUE});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]
                {BlackjackCard.KING_VALUE, 6, BlackjackCard.JACK_VALUE};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, BlackjackCard.ACE_VALUE, 10, 5, 8, 6});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]{BlackjackCard.ACE_VALUE, 5, 8, 6};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 5, 10, BlackjackCard.ACE_VALUE, BlackjackCard.ACE_VALUE});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]
                {5, BlackjackCard.ACE_VALUE, BlackjackCard.ACE_VALUE};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{10, 6, 5, 10, 10});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.hit();

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]{6, 10};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{10, 6, 5, 10, 6, 4});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.hit();

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]{6, 10, 4};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{10, 6, BlackjackCard.ACE_VALUE, 10});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.playDealersHand();

        expectedDealersHandCardValues = new int[]{6, 10};
        assertTrue(BlackjackInitialTester.cardValuesMatchExpected(
                expectedDealersHandCardValues, bj.getDealersHand().getCards()));
    }

    @Test
    public void testPayouts()
    {
        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 7, 10, 10});

        Blackjack bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        bj.resolveBetsAndReset();

        assertEquals(1010, bj.getPlayersMoney(), MONEY_TOLERANCE);


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 6, 10, 10, 9});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        bj.resolveBetsAndReset();
        
        assertEquals(1010, bj.getPlayersMoney(), MONEY_TOLERANCE);


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {BlackjackCard.ACE_VALUE, 8, 10, 10});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        bj.resolveBetsAndReset();

        assertEquals(1015, bj.getPlayersMoney(), MONEY_TOLERANCE);


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {BlackjackCard.ACE_VALUE, 10, 10, BlackjackCard.ACE_VALUE});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        bj.resolveBetsAndReset();
        
        assertEquals(1000, bj.getPlayersMoney(), MONEY_TOLERANCE);


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{10, 8, 8, 10});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        bj.resolveBetsAndReset();
        
        assertEquals(1000, bj.getPlayersMoney(), MONEY_TOLERANCE);
    }

    @Test
    public void testOutcomeChecks()
    {
        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]{
                BlackjackCard.ACE_VALUE,
                BlackjackCard.KING_VALUE,
                BlackjackCard.QUEEN_VALUE,
                BlackjackCard.ACE_VALUE});

        Blackjack bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        
        assertEquals(Blackjack.PUSH_RESULT, bj.getResult());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {8, 6, 9, BlackjackCard.ACE_VALUE});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        
        assertEquals(Blackjack.PUSH_RESULT, bj.getResult());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {5, 9, 8, 6, 4, 2});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.hit();
        bj.playDealersHand();
        
        assertEquals(Blackjack.PUSH_RESULT, bj.getResult());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{10, 9, 9, 10});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();

        assertEquals(Blackjack.PUSH_RESULT, bj.getResult());


//         must remove canHit() precondition from hit() to test this
//        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
//                {10, BlackjackCard.ACE_VALUE, 5, BlackjackCard.QUEEN_VALUE, 6});
//        
//        bj = new Blackjack(1000);
//        bj.placeBetAndDealCards(10);
//        bj.hit();
//        bj.playDealersHand();
//
//        assertEquals(Blackjack.LOSS_RESULT, bj.getResult());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {BlackjackCard.QUEEN_VALUE, 10, BlackjackCard.ACE_VALUE, 6, 5});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        
        assertEquals(Blackjack.WIN_BJ_RESULT, bj.getResult());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {BlackjackCard.QUEEN_VALUE, 10, BlackjackCard.JACK_VALUE, 9});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        
        assertEquals(Blackjack.WIN_RESULT, bj.getResult());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(new int[]
                {10, 10, 9, BlackjackCard.JACK_VALUE});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        
        assertEquals(Blackjack.LOSS_RESULT, bj.getResult());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 6, 10, 10, 9});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        
        assertEquals(Blackjack.WIN_RESULT, bj.getResult());


        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{6, 10, 10, 7, 9});

        bj = new Blackjack(1000);
        bj.placeBetAndDealCards(10);

        bj.hit();

        bj.playDealersHand();
        
        assertEquals(Blackjack.LOSS_RESULT, bj.getResult());
    }
    
    @Test
    public void testReset()
    {
        final int MAX_CARDS_TO_RESET = 77;
        final int CARDS_TO_BE_USED = 5;
        
        
        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 6, 10, 10, 9});
        
        while(TestShoe.cards.size() < MAX_CARDS_TO_RESET + CARDS_TO_BE_USED)
            TestShoe.cards.add(0, new BlackjackCard(9, "C"));

        Blackjack bj = new Blackjack(1000);
        
        TestShoe.resetRun = false;
        
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        bj.resolveBetsAndReset();
        
        assertTrue(TestShoe.resetRun);
        
        try
        {
            bj.getPlayersBet();
            assertTrue(false); // should have thrown exception
        }
        catch(IllegalStateException e)
        {
            assertTrue(true);
        }
        
        
        TestShoe.cards = BlackjackInitialTester.getCardListInReverseOrder(
                new int[]{8, 6, 10, 10, 9});
        
        while(TestShoe.cards.size() < MAX_CARDS_TO_RESET + 1 + CARDS_TO_BE_USED)
            TestShoe.cards.add(0, new BlackjackCard(9, "C"));

        bj = new Blackjack(1000);
        
        TestShoe.resetRun = false;
        
        bj.placeBetAndDealCards(10);
        bj.playDealersHand();
        bj.resolveBetsAndReset();
        
        assertTrue( ! TestShoe.resetRun );
        
        try
        {
            bj.getPlayersBet();
            assertTrue(false); // should have thrown exception
        }
        catch(IllegalStateException e)
        {
            assertTrue(true);
        }
    }
}
