package com.ghost.technic.server.card.playable.usable.impl;

import com.ghost.technic.server.Player;
import com.ghost.technic.server.board.Flank;
import com.ghost.technic.server.card.keyword.Keyword;
import com.ghost.technic.server.card.keyword.KeywordAbility;
import com.ghost.technic.server.card.playable.PlayableCard;
import com.ghost.technic.server.card.playable.impl.UpgradeCard;
import com.ghost.technic.server.card.playable.usable.Usable;
import com.ghost.technic.server.counter.Counters;
import com.ghost.technic.server.game.GameState;
import com.ghost.technic.server.game.event.*;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Data
@Slf4j
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
public class CreatureCard extends PlayableCard implements Usable {
    private int power;
    private int armor;
    private boolean ready;
    private List<String> traits;
    @Builder.Default
    private Counters counters = new Counters();
    @Builder.Default
    private Set<Keyword> keywords = new HashSet<>();
    @Builder.Default
    private List<UpgradeCard> upgrades = new ArrayList<>();

    public void play(Player player, Set<Flank> flanks, EventBus eventBus) {
        publishUsedEvent(player, UsageType.PLAY, flanks, eventBus);
    }

    public void publishUsedEvent(Player player, UsageType usageType, Set<Flank> flanks, EventBus eventBus) {
        UsedEventData usedData = new UsedEventData(this, usageType);
        Map<String, Object> payload = Map.of(
                "usedEventData", usedData,
                "flanks", flanks,
                "player", player);
        GameEvent usedEvent = new GameEvent(EventType.USED, payload);
        eventBus.publish(usedEvent);
        log.info("{} {} {}", player.getName(), usageType, getName());
    }

    public int getDamage() {
        return counters.getDmg();
    }

    public void takeDamage(int amount) {
        counters.addDmg(amount);
    }

    public boolean isDestroyed() {
        return counters.getDmg() >= power;
    }

    public void beforeFight() {
        log.info("{} triggers before fight effects.", getName());
    }

    public void afterFight() {
        log.info("{} triggers after fight effects.", getName());
    }

    public void beforeReap() {
        log.info("{} triggers before reap effects.", getName());
    }

    public void fight(Player player, EventBus eventBus) {
        publishUsedEvent(player, UsageType.FIGHT, eventBus);
    }

    public void reap(Player player, EventBus eventBus) {
        publishUsedEvent(player, UsageType.REAP, eventBus);
    }

    public void afterReap() {
        log.info("{} triggers after reap effects.", getName());
    }

    public boolean hasKeyword(KeywordAbility keywordAbility) {
        return keywords.stream().anyMatch(k -> k.getKeywordAbility() == keywordAbility);
    }

    public Optional<Keyword> getKeyword(KeywordAbility keywordAbility) {
        return keywords.stream().filter(k -> k.getKeywordAbility() == keywordAbility).findFirst();
    }

    public int getEffectiveArmor(GameState state) {
        return armor + state.getArmorBuffs(this);
    }

    public int getKeywordValue(KeywordAbility ability) {
        return keywords.stream()
                .filter(k -> k.getKeywordAbility() == ability)
                .map(Keyword::getValue)
                .findFirst()
                .orElse(0);
    }
}
