import random
import unittest

from hearthbreaker.agents.basic_agents import DoNothingAgent, PredictableAgent
from tests.agents.testing_agents import OneCardPlayingAgent, PlayAndAttackAgent, CardTestingAgent,\
    SelfSpellTestingAgent, EnemyMinionSpellTestingAgent
from tests.testing_utils import generate_game_for
from hearthbreaker.cards import *


class TestWarrior(unittest.TestCase):
    def setUp(self):
        random.seed(1857)

    def test_ArathiWeaponsmith(self):
        game = generate_game_for(ArathiWeaponsmith, StonetuskBoar, OneCardPlayingAgent, DoNothingAgent)

        # Arathi Weaponsmith should be played
        for turn in range(0, 7):
            game.play_single_turn()

        self.assertEqual(1, len(game.players[0].minions))
        self.assertEqual(3, game.players[0].minions[0].calculate_attack())
        self.assertEqual(3, game.players[0].minions[0].health)
        self.assertEqual("Arathi Weaponsmith", game.players[0].minions[0].card.name)
        self.assertEqual(2, game.players[0].hero.weapon.base_attack)
        self.assertEqual(2, game.players[0].hero.weapon.durability)

    def test_Armorsmith(self):
        game = generate_game_for(Armorsmith, StonetuskBoar, OneCardPlayingAgent, PlayAndAttackAgent)

        # Armorsmith should be played
        for turn in range(0, 3):
            game.play_single_turn()

        self.assertEqual(1, len(game.players[0].minions))
        self.assertEqual(1, game.players[0].minions[0].calculate_attack())
        self.assertEqual(4, game.players[0].minions[0].health)
        self.assertEqual("Armorsmith", game.players[0].minions[0].card.name)
        self.assertEqual(0, game.players[0].hero.armor)

        # Three Stonetusks should attack, generating one armor each
        game.play_single_turn()
        self.assertEqual(1, game.players[0].minions[0].health)
        self.assertEqual(3, game.players[0].hero.armor)

    def test_CruelTaskmaster(self):
        game = generate_game_for(CruelTaskmaster, Shieldbearer, OneCardPlayingAgent, OneCardPlayingAgent)

        for turn in range(0, 2):
            game.play_single_turn()

        self.assertEqual(1, len(game.players[1].minions))
        self.assertEqual(0, game.players[1].minions[0].calculate_attack())
        self.assertEqual(4, game.players[1].minions[0].health)

        # Cruel Taskmaster should be played, targeting the Shieldbearer
        game.play_single_turn()
        self.assertEqual(1, len(game.players[0].minions))
        self.assertEqual(2, game.players[0].minions[0].calculate_attack())
        self.assertEqual(2, game.players[0].minions[0].health)
        self.assertEqual("Cruel Taskmaster", game.players[0].minions[0].card.name)
        self.assertEqual(1, len(game.players[1].minions))
        self.assertEqual(2, game.players[1].minions[0].calculate_attack())
        self.assertEqual(3, game.players[1].minions[0].health)

        game.play_single_turn()
        game.play_single_turn()

        self.assertEqual(2, len(game.players[0].minions))
        self.assertEqual(2, len(game.players[1].minions))
        self.assertEqual(2, game.players[0].minions[0].calculate_attack())
        self.assertEqual(2, game.players[0].minions[0].health)
        self.assertEqual(2, game.players[0].minions[1].calculate_attack())
        self.assertEqual(2, game.players[0].minions[1].health)
        self.assertEqual(2, game.players[1].minions[0].calculate_attack())
        self.assertEqual(3, game.players[1].minions[0].health)
        self.assertEqual(2, game.players[1].minions[1].calculate_attack())
        self.assertEqual(3, game.players[1].minions[1].health)

    def test_FrothingBerserker(self):
        game = generate_game_for(FrothingBerserker, AngryChicken, OneCardPlayingAgent, PlayAndAttackAgent)

        for turn in range(0, 4):
            game.play_single_turn()

        self.assertEqual(3, len(game.players[1].minions))

        # Frothing Berserker should be played
        game.play_single_turn()
        self.assertEqual(1, len(game.players[0].minions))
        self.assertEqual(2, game.players[0].minions[0].calculate_attack())
        self.assertEqual(4, game.players[0].minions[0].health)
        self.assertEqual("Frothing Berserker", game.players[0].minions[0].card.name)

        # Three chickens should attack, generating a total of +6 attack for the Frothing Berserker
        game.play_single_turn()
        self.assertEqual(8, game.players[0].minions[0].calculate_attack())
        self.assertEqual(1, game.players[0].minions[0].health)

    def test_GrommashHellscream(self):
        game = generate_game_for(GrommashHellscream, ExplosiveTrap, PlayAndAttackAgent, CardTestingAgent)

        for turn in range(0, 14):
            game.play_single_turn()

        # Hellscream should be played, attacking (charge) and getting 2 damage by trap that will trigger enrage,
        # dealing 10 damage as result
        game.play_single_turn()
        self.assertEqual(1, len(game.players[0].minions))
        self.assertEqual(10, game.players[0].minions[0].calculate_attack())
        self.assertEqual(7, game.players[0].minions[0].health)
        self.assertEqual(20, game.players[1].hero.health)

        game.players[0].minions[0].heal(2, None)
        self.assertEqual(4, game.players[0].minions[0].calculate_attack())
        game.players[0].minions[0].damage(2, None)
        self.assertEqual(10, game.players[0].minions[0].calculate_attack())

        game.players[0].minions[0].silence()
        self.assertEqual(4, game.players[0].minions[0].calculate_attack())
        game.players[0].minions[0].heal(2, None)
        self.assertEqual(4, game.players[0].minions[0].calculate_attack())
        game.players[0].minions[0].damage(2, None)
        self.assertEqual(4, game.players[0].minions[0].calculate_attack())

    def test_KorkronElite(self):
        game = generate_game_for(KorkronElite, StonetuskBoar, PlayAndAttackAgent, DoNothingAgent)

        for turn in range(0, 6):
            game.play_single_turn()

        # Kor'kron Elite should be played and attack (charge)
        game.play_single_turn()
        self.assertEqual(1, len(game.players[0].minions))
        self.assertEqual(4, game.players[0].minions[0].calculate_attack())
        self.assertEqual(3, game.players[0].minions[0].health)
        self.assertEqual(26, game.players[1].hero.health)

    def test_WarsongCommander(self):
        game = generate_game_for(WarsongCommander, StonetuskBoar, PlayAndAttackAgent, DoNothingAgent)

        # Super special test cases - http://www.hearthhead.com/card=1009/warsong-commander#comments:id=1935295
        game.players[0].mana = 100

        # Play the Warsong Commander
        commander = WarsongCommander()
        commander.player = game.players[0]
        commander.use(game.players[0], game)
        self.assertFalse(game.players[0].minions[0].charge())  # Should not give charge to itself

        # Test so that enrage doesn't remove the charge
        worgen = RagingWorgen()
        worgen.player = game.players[0]
        worgen.use(game.players[0], game)
        game.players[0].minions[0].damage(1, None)  # Trigger enrage, charge should still be active
        self.assertEqual(4, game.players[0].minions[0].calculate_attack())
        self.assertTrue(game.players[0].minions[0].charge())

        # Test so that charge gets applied before a battlecry
        weapon = FieryWarAxe().create_weapon(game.players[0])
        weapon.equip(game.players[0])
        self.assertEqual(3, game.players[0].hero.weapon.base_attack)
        self.assertEqual(2, game.players[0].hero.weapon.durability)
        bloodsail = BloodsailRaider()
        bloodsail.player = game.players[0]
        bloodsail.use(game.players[0], game)  # Should gain charge first, then 4 attack from weapon
        self.assertEqual(5, game.players[0].minions[0].calculate_attack())
        self.assertTrue(game.players[0].minions[0].charge())

        # TODO: Test with Faceless Manipulator here

        # Remove the Warsong Commander
        game.players[0].minions[-1].die(None)
        game.check_delayed()
        # The previous charged minions should still have charge
        self.assertTrue(game.players[0].minions[0].charge())