package com.ghost.technic.server.card;

import com.ghost.technic.server.card.effect.impl.RainceFuryheartAbility;
import com.ghost.technic.server.card.effect.impl.WayOfTheBearAbility;
import com.ghost.technic.server.card.keyword.BeforeFightEffect;
import com.ghost.technic.server.card.keyword.Keyword;
import com.ghost.technic.server.card.keyword.KeywordAbility;
import com.ghost.technic.server.card.playable.impl.UpgradeCard;
import com.ghost.technic.server.counter.Counters;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.ghost.technic.server.Player;
import com.ghost.technic.server.board.Flank;
import com.ghost.technic.server.card.playable.usable.impl.CreatureCard;
import com.ghost.technic.server.game.GameActionService;
import com.ghost.technic.server.game.GameState;
import com.ghost.technic.server.game.event.EventBus;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

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

class RainceFuryheartAndWayOfTheBearBeforeFightTest {

    GameActionService service;
    Player active;
    Player defenderPlayer;
    CreatureCard raince;
    CreatureCard defender;
    UpgradeCard wayOfTheBear;

    @BeforeEach
    void setUp() {
        service = new GameActionService(new GameState(new EventBus()));

        defenderPlayer = Player.builder().name("Bob").build();

        // Defender: 2 power, Hazardous 2
        defender = CreatureCard.builder()
                .name("Hazard Goblin")
                .power(2)
                .armor(0)
                .keywords(new HashSet<>(Set.of(new Keyword(KeywordAbility.HAZARDOUS, 3))))
                .counters(new Counters())
                .build();

        raince = CreatureCard.builder()
                .name("Raince Furyheart")
                .power(3)
                .armor(0)
                .keywords(new HashSet<>(Set.of(new Keyword(KeywordAbility.SKIRMISH))))
                .counters(new Counters())
                .abilities(List.of(new RainceFuryheartAbility()))
                .build();

        wayOfTheBear = UpgradeCard.builder()
                .name("Way of the Bear")
                .abilities(List.of(new WayOfTheBearAbility()))
                .build();

        service.getGameState().setOwner(raince, active);
        service.getGameState().setOwner(defender, defenderPlayer);
    }

    @Test
    void testEffectOrder_RainceThenAssaultThenHazardous() {

        active = Player.builder().name("Alice").build();

        active = new Player(active) {
            @Override
            public List<BeforeFightEffect> chooseBeforeFightOrder(List<BeforeFightEffect> effects) {
                return List.of(
                        desc(effects, "Exalt the creature Raince Furyheart fights"),
                        desc(effects, "Assault from Raince Furyheart"),
                        desc(effects, "Hazardous from Hazard Goblin")
                );
            }
        };
        service.playCreature(active, raince, Flank.LEFT);
        service.getGameState().setPlayers(active, defenderPlayer);
        service.getGameState().setActivePlayer(active);
        service.playUpgrade(active, wayOfTheBear, raince);

        assertTrue(raince.hasKeyword(KeywordAbility.ASSAULT), "Raince should have Assault after Way of the Bear");

        service.fight(raince, defender, active);

        assertEquals(1, defender.getCounters().getAmber(), "Defender should be exalted");
        assertTrue(defender.isDestroyed(), "Defender should be destroyed by Assault");
        assertFalse(raince.isDestroyed(), "Raince should survive");
    }

    @Test
    void testEffectOrder_HazardousFirst() {

        active = Player.builder().name("Alice").build();

        active = new Player(active) {
            @Override
            public List<BeforeFightEffect> chooseBeforeFightOrder(List<BeforeFightEffect> effects) {
                return List.of(
                        desc(effects, "Hazardous from Hazard Goblin"),
                        desc(effects, "Assault from Raince Furyheart"),
                        desc(effects, "Exalt the creature Raince Furyheart fights")
                );
            }
        };
        service.playCreature(active, raince, Flank.LEFT);
        service.getGameState().setPlayers(active, defenderPlayer);
        service.getGameState().setActivePlayer(active);
        service.playUpgrade(active, wayOfTheBear, raince);
        service.fight(raince, defender, active);

        assertEquals(0, defender.getCounters().getAmber(), "Defender should NOT be exalted if Raince dies first");
        assertTrue(raince.isDestroyed(), "Raince should be destroyed by Hazardous before fight");
        assertFalse(defender.isDestroyed(), "Defender should survive");
    }

    @Test
    void testEffectOrder_AssaultThenHazardousThenRaince() {

        active = Player.builder().name("Alice").build();

        active = new Player(active) {
            @Override
            public List<BeforeFightEffect> chooseBeforeFightOrder(List<BeforeFightEffect> effects) {
                return List.of(
                        desc(effects, "Assault from Raince Furyheart"),
                        desc(effects, "Hazardous from Hazard Goblin"),
                        desc(effects, "Exalt the creature Raince Furyheart fights")
                );
            }
        };
        service.playCreature(active, raince, Flank.LEFT);
        service.getGameState().setPlayers(active, defenderPlayer);
        service.getGameState().setActivePlayer(active);
        service.playUpgrade(active, wayOfTheBear, raince);
        service.fight(raince, defender, active);

        assertEquals(0, defender.getCounters().getAmber(), "Exalt should not have resolved because assault would have killed defender first");
        assertTrue(defender.isDestroyed(), "Defender should die from Assault");
        assertFalse(raince.isDestroyed(), "Raince would survive because the assault would kill defender first");
    }

    private BeforeFightEffect desc(List<BeforeFightEffect> list, String text) {
        return list.stream()
                .filter(e -> e.description().equals(text))
                .findFirst()
                .orElseThrow(() -> new AssertionError("Missing effect: " + text));
    }
}
