package org.mage.test.cards.abilities.keywords;

import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.abilities.keyword.LifelinkAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.filter.Filter;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;

/**
 * @author noxx
 */
public class SoulbondKeywordTest extends CardTestPlayerBase {

    @Test
    public void testPairOnCast() {
        addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");

        setStopAt(1, PhaseStep.BEGIN_COMBAT);
        execute();

        assertPermanentCount(playerA, "Trusted Forcemage", 1);
        assertPowerToughness(playerA, "Trusted Forcemage", 3, 3);
        assertPowerToughness(playerA, "Elite Vanguard", 3, 2);
    }

    /**
     * Tests pair on another creature without Soulbond entering battlefield
     */
    @Test
    public void testPairOnEntersBattlefield() {
        addCard(Zone.HAND, playerA, "Elite Vanguard");
        addCard(Zone.BATTLEFIELD, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Vanguard");

        setStopAt(1, PhaseStep.BEGIN_COMBAT);
        execute();

        assertPermanentCount(playerA, "Trusted Forcemage", 1);
        assertPowerToughness(playerA, "Trusted Forcemage", 3, 3);
        assertPowerToughness(playerA, "Elite Vanguard", 3, 2);
    }

    /**
     * Tests two creatures with Soulbond paired with each other
     */
    @Test
    public void testTwoSoulbondCreaturesOnBattlefield() {
        addCard(Zone.HAND, playerA, "Trusted Forcemage", 2);
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 6);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");

        setStopAt(1, PhaseStep.BEGIN_COMBAT);
        execute();

        assertPermanentCount(playerA, "Trusted Forcemage", 2);
        assertPowerToughness(playerA, "Trusted Forcemage", 4, 4, Filter.ComparisonScope.All);
    }

    /**
     * Tests no Soulbond effect possible on single creature
     */
    @Test
    public void testNoPairOnSingleCreature() {
        addCard(Zone.HAND, playerA, "Trusted Forcemage", 1);
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");

        setStopAt(1, PhaseStep.BEGIN_COMBAT);
        execute();

        assertPermanentCount(playerA, "Trusted Forcemage", 1);
        assertPowerToughness(playerA, "Trusted Forcemage", 2, 2);
    }

    /**
     * Tests Soulbond effect disabling whenever Soulbond creature changes its
     * controller
     */
    @Test
    public void testChangeControllerForSoulbondCreature() {
        addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
        // Soulbond (You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them.)
        // As long as Trusted Forcemage is paired with another creature, each of those creatures gets +1/+1.
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);

        // Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. (It can attack and Tap this turn.)
        addCard(Zone.HAND, playerB, "Act of Treason");
        addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Act of Treason", "Trusted Forcemage");

        setStopAt(2, PhaseStep.BEGIN_COMBAT);
        execute();

        assertPermanentCount(playerA, "Trusted Forcemage", 0);
        assertPermanentCount(playerA, "Elite Vanguard", 1);
        assertPowerToughness(playerA, "Elite Vanguard", 2, 1);

        assertPermanentCount(playerB, "Trusted Forcemage", 1);
        assertPowerToughness(playerB, "Trusted Forcemage", 2, 2);

    }

    /**
     * Tests Soulbond effect disabling when paired creture changes its
     * controller
     */
    @Test
    public void testChangeControllerForAnotherCreature() {
        addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); // 2,1
        // Soulbond (You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them.)
        // As long as Trusted Forcemage is paired with another creature, each of those creatures gets +1/+1.
        addCard(Zone.HAND, playerA, "Trusted Forcemage"); // 2/2
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);

        addCard(Zone.HAND, playerB, "Act of Treason");
        addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Act of Treason", "Elite Vanguard");

        setStopAt(2, PhaseStep.BEGIN_COMBAT);
        execute();

        assertPermanentCount(playerA, "Trusted Forcemage", 1);
        assertPermanentCount(playerA, "Elite Vanguard", 0);
        assertPowerToughness(playerA, "Trusted Forcemage", 2, 2);

        assertPermanentCount(playerB, "Elite Vanguard", 1);
        assertPowerToughness(playerB, "Elite Vanguard", 2, 1);
    }

    /**
     * Tests Soulbond effect disabling when Soulbond creature changes its
     * controller and then returns back. Effect should not be restored.
     */
    @Test
    public void testChangeControllerAndGettingBack() {
        addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);

        addCard(Zone.HAND, playerB, "Act of Treason");
        addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Act of Treason", "Trusted Forcemage");

        setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
        execute();

        assertPermanentCount(playerA, "Trusted Forcemage", 1);
        assertPowerToughness(playerA, "Trusted Forcemage", 2, 2);
        assertPermanentCount(playerA, "Elite Vanguard", 1);
        assertPowerToughness(playerA, "Elite Vanguard", 2, 1);
    }

    /**
     * Tests that stealing creature will allow to use Soulbond ability on
     * controller's creature
     */
    @Test
    public void testSoulbondWorksOnControllerSide() {
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);

        addCard(Zone.HAND, playerB, "Act of Treason");
        addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);
        addCard(Zone.HAND, playerB, "Elite Vanguard");
        addCard(Zone.BATTLEFIELD, playerB, "Plains", 3);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Act of Treason", "Trusted Forcemage");
        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Elite Vanguard");

        setStopAt(2, PhaseStep.BEGIN_COMBAT);
        execute();

        // stolen
        assertPermanentCount(playerA, "Trusted Forcemage", 0);

        // both paired and have boost
        assertPowerToughness(playerB, "Trusted Forcemage", 3, 3);
        assertPowerToughness(playerB, "Elite Vanguard", 3, 2);
    }

    /**
     * Tests effect also disappeared when creature is returned back to owner
     */
    @Test
    public void testReturnBack() {
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);

        addCard(Zone.HAND, playerB, "Act of Treason");
        addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3);
        addCard(Zone.HAND, playerB, "Elite Vanguard");
        addCard(Zone.BATTLEFIELD, playerB, "Plains", 3);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Act of Treason", "Trusted Forcemage");
        castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Elite Vanguard");

        setStopAt(3, PhaseStep.PRECOMBAT_MAIN); // Effect of "Act of Treason" will end here
        execute();

        // returned back with no boost
        assertPermanentCount(playerA, "Trusted Forcemage", 1);
        assertPowerToughness(playerA, "Trusted Forcemage", 2, 2);

        // no boost on next turn (gets unpaired)
        assertPowerToughness(playerB, "Elite Vanguard", 2, 1);
    }

    /**
     * Tests returning one of the paired creatures back to its owner's hand
     */
    @Test
    public void testUnsummon() {
        addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard");
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
        addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
        addCard(Zone.HAND, playerA, "Unsummon", 1);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unsummon", "Elite Vanguard");

        setStopAt(1, PhaseStep.BEGIN_COMBAT);
        execute();

        assertPermanentCount(playerA, "Elite Vanguard", 0);
        assertPowerToughness(playerA, "Trusted Forcemage", 2, 2);
    }

    /**
     * Tests that it is possible to animate land and pair it on next coming
     * Soulbond creature
     */
    @Test
    public void testPairOnAnimatedLand() {
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
        addCard(Zone.BATTLEFIELD, playerA, "Blinkmoth Nexus", 1);

        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: ");
        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");

        setStopAt(1, PhaseStep.BEGIN_COMBAT);
        execute();

        // test paired with boost
        assertPowerToughness(playerA, "Trusted Forcemage", 3, 3);
        assertPowerToughness(playerA, "Blinkmoth Nexus", 2, 2);
    }

    /**
     * Tests no effect whether land was animated after Soulbond creature has
     * entered the battlefield
     */
    @Test
    public void testPairOnPostAnimatedLand() {
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
        addCard(Zone.BATTLEFIELD, playerA, "Blinkmoth Nexus", 1);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{1}: ");

        setStopAt(1, PhaseStep.DECLARE_ATTACKERS);
        execute();

        // no effect on later animation
        assertPowerToughness(playerA, "Trusted Forcemage", 2, 2);
        assertPowerToughness(playerA, "Blinkmoth Nexus", 1, 1);
    }

    /**
     * Tests that creature type loss leads to Soulbond effect disabling
     */
    @Test
    public void testCreatureTypeLoss() {
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 4);
        addCard(Zone.BATTLEFIELD, playerA, "Blinkmoth Nexus", 1);

        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: ");
        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");

        setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
        execute();

        // test boost loss
        assertPowerToughness(playerA, "Trusted Forcemage", 2, 2);
    }

    /**
     * Tests that after loosing first pair it is possible to pair creature with
     * another one
     */
    @Test
    public void testRebondOnNextCreature() {
        // When Phantasmal Bear becomes the target of a spell or ability, sacrifice it.
        addCard(Zone.HAND, playerA, "Phantasmal Bear");
        // Soulbond
        // As long as Trusted Forcemage is paired with another creature, each of those creatures gets +1/+1.
        addCard(Zone.HAND, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); // 2/1
        addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
        addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
        addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
        addCard(Zone.BATTLEFIELD, playerA, "Island", 3);

        addCard(Zone.HAND, playerA, "Lightning Bolt", 1);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Trusted Forcemage");
        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Elite Vanguard");
        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Phantasmal Bear");

        setStopAt(1, PhaseStep.END_TURN);
        execute();

        assertPermanentCount(playerA, "Elite Vanguard", 0);
        assertPermanentCount(playerA, "Phantasmal Bear", 1);

        assertPowerToughness(playerA, "Trusted Forcemage", 3, 3);
        assertPowerToughness(playerA, "Phantasmal Bear", 3, 3);
    }

    /**
     * Tests Soulbond that adds an ability to both creatures
     */
    @Test
    public void testGrantingAbility() {
        addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); // 2/1
        // Soulbond
        // As long as Nearheath Pilgrim is paired with another creature, both creatures have lifelink.
        addCard(Zone.HAND, playerA, "Nearheath Pilgrim");
        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nearheath Pilgrim");

        setStopAt(1, PhaseStep.BEGIN_COMBAT);
        execute();

        assertPowerToughness(playerA, "Nearheath Pilgrim", 2, 1);
        assertPowerToughness(playerA, "Elite Vanguard", 2, 1);

        Abilities<Ability> abilities = new AbilitiesImpl<>();
        abilities.add(LifelinkAbility.getInstance());
        assertAbilities(playerA, "Nearheath Pilgrim", abilities);
        assertAbilities(playerA, "Elite Vanguard", abilities);
    }

    @Test
    public void testExileAndReturnBack() {
        addCard(Zone.HAND, playerA, "Elite Vanguard");
        addCard(Zone.HAND, playerA, "Cloudshift");
        // Soulbond (You may pair this creature with another unpaired creature when either enters the battlefield. They remain paired for as long as you control both of them.)
        // As long as Trusted Forcemage is paired with another creature, each of those creatures gets +1/+1.
        addCard(Zone.BATTLEFIELD, playerA, "Trusted Forcemage");
        addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Vanguard");
        setChoice(playerA, "Yes");
        castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cloudshift", "Trusted Forcemage");
        setChoice(playerA, "No");

        setStopAt(1, PhaseStep.END_TURN);
        execute();

        assertPermanentCount(playerA, "Trusted Forcemage", 1);
        assertPowerToughness(playerA, "Trusted Forcemage", 2, 2);
        assertPowerToughness(playerA, "Elite Vanguard", 2, 1);

        Permanent trustedForcemange = getPermanent("Trusted Forcemage", playerA.getId());
        Permanent eliteVanguard = getPermanent("Elite Vanguard", playerA.getId());
        Assert.assertEquals(trustedForcemange.getPairedCard(), null);
        Assert.assertEquals(eliteVanguard.getPairedCard(), null);
    }

    /*
     * Reported bug: Soulbond should use the stack, but unable to use instant speed removal since no trigger occurs
     */
    @Test
    // Soulbond does not currently use the stack, so this test will fail until then
    public void testRespondToSoulboundWithRemoval() {

        // When Palinchron enters the battlefield, untap up to seven lands.
        // {2}{U}{U}: Return Palinchron to its owner's hand.
        addCard(Zone.BATTLEFIELD, playerA, "Palinchron"); // 4/5 flying

        // Soulbond
        // As long as Deadeye Navigator is paired with another creature, each of those creatures has "{1}{U}: Exile this creature, then return it to the battlefield under your control."
        addCard(Zone.HAND, playerA, "Deadeye Navigator"); // 5/5
        addCard(Zone.BATTLEFIELD, playerA, "Island", 8);
        addCard(Zone.HAND, playerB, "Doom Blade", 1); // {1}{B} instant: Destroy target non-black creature
        addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);

        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Deadeye Navigator");
        setChoice(playerA, "Yes");
        setChoice(playerA, "Palinchron");
        castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Doom Blade", "Deadeye Navigator");

        // Deadeye's ability should not be usable since was destroyed before Soulbond trigger resolved
        activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}:");
        setStopAt(1, PhaseStep.BEGIN_COMBAT);
        execute();

        Permanent palinchron = getPermanent("Palinchron", playerA);
        Assert.assertEquals(null, palinchron.getPairedCard()); // should not be paired
        assertGraveyardCount(playerA, "Deadeye Navigator", 1);
        assertGraveyardCount(playerB, "Doom Blade", 1);
        assertPermanentCount(playerA, "Palinchron", 1);
    }
}
