package com.ghost.technic.server.game;

import com.ghost.technic.server.card.misc.House;
import com.ghost.technic.server.card.playable.usable.impl.CreatureCard;
import com.ghost.technic.server.counter.Counters;
import com.ghost.technic.server.game.GameActionService;
import com.ghost.technic.server.game.GameState;
import com.ghost.technic.server.game.event.EventBus;
import com.ghost.technic.server.game.event.EventType;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Slf4j
public class DealingDamageTest {

    private GameActionService gameActionService;
    private CreatureCard creature;

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

        creature = CreatureCard.builder()
                .name("Sacro-Beast")
                .house(House.UNTAMED)
                .traits(List.of("Mutant", "Beast"))
                .power(5)
                .armor(2)
                .counters(new Counters())
                .build();

//        log.info(creature.toString());
    }

    @Test
    void testFullArmorAbsorbsAllDamage() {
        AtomicBoolean preventedTriggered = new AtomicBoolean(false);

        gameActionService.getEventBus().subscribe(EventType.DAMAGE_PREVENTED, event -> {
            CreatureCard c = event.get("creature");
            int amount = event.get("amount");
            assertEquals("Sacro-Beast", c.getName());
            assertEquals(2, amount);
            preventedTriggered.set(true);
        });

        gameActionService.dealDamage(creature, 2);

        assertEquals(0, creature.getCounters().getDmg(), "No damage should be on the creature");
        assertEquals(0, creature.getArmor(), "All Armor should be consumed");
        assertTrue(preventedTriggered.get(), "DAMAGE_PREVENTED event should be triggered");
    }

    @Test
    void testPartialArmorPartialDamage() {
        AtomicInteger damageTaken = new AtomicInteger();
        AtomicInteger damagePrevented = new AtomicInteger();

        gameActionService.getEventBus().subscribe(EventType.DAMAGE_PREVENTED, event -> {
            damagePrevented.set(event.get("amount"));
        });

        gameActionService.getEventBus().subscribe(EventType.DAMAGE_TAKEN, event -> {
            damageTaken.set(event.get("amount"));
        });

        gameActionService.dealDamage(creature, 4); // 2 absorbed, 2 to damage

        assertEquals(2, creature.getCounters().getDmg(), "Creature should have 2 damage");
        assertEquals(0, creature.getArmor(), "Armor should be reduced to 0");

        assertEquals(2, damagePrevented.get(), "2 damage should be prevented");
        assertEquals(2, damageTaken.get(), "2 damage should be taken");
    }

    @Test
    void testNoArmorTakesAllDamage() {
        AtomicInteger damageTaken = new AtomicInteger();

        creature.setArmor(0); // no Armor

        gameActionService.getEventBus().subscribe(EventType.DAMAGE_TAKEN, event -> {
            damageTaken.set(event.get("amount"));
        });

        gameActionService.dealDamage(creature, 3);

        assertEquals(3, creature.getCounters().getDmg(), "All 3 damage should be applied");
        assertEquals(3, damageTaken.get(), "DAMAGE_TAKEN should show full amount");
    }

    @Test
    void testZeroDamageStillFiresPreventIfArmor() {
        AtomicBoolean preventedTriggered = new AtomicBoolean(false);

        gameActionService.getEventBus().subscribe(EventType.DAMAGE_PREVENTED, event -> {
            preventedTriggered.set(true);
            assertEquals(0, (int) event.get("amount"));
        });

        gameActionService.dealDamage(creature, 0);

        assertEquals(2, creature.getArmor(), "Armor should remain untouched");
        assertEquals(0, creature.getCounters().getDmg(), "No damage should be dealt");
        assertTrue(preventedTriggered.get(), "Prevented event should still fire with zero dmg");
    }
}
