package com.ghost.technic.server.game;

import com.ghost.technic.server.Player;
import com.ghost.technic.server.card.effect.ConstantAbility;
import com.ghost.technic.server.card.misc.House;
import com.ghost.technic.server.card.playable.PlayableCard;
import com.ghost.technic.server.card.playable.usable.impl.CreatureCard;
import com.ghost.technic.server.game.event.EventBus;
import com.ghost.technic.server.game.event.EventType;
import com.ghost.technic.server.game.event.UsedEventData;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Data
@Slf4j
@RequiredArgsConstructor
public class GameState {

    private Player playerOne;
    private Player playerTwo;
    private Player activePlayer;
    private int turnCount = 0;
    private Map<PlayableCard, ConstantAbility> staticEffects = new HashMap<>();
    private Map<PlayableCard, Player> cardOwnership = new HashMap<>();
    private Map<String, Integer> ruleOfSixCounter = new HashMap<>();
    private final EventBus eventBus;
    private final Map<Player, Set<House>> nextTurnHouseRestrictions = new HashMap<>();
    private final Map<Player, House> forcedHouseChoiceNextTurn = new HashMap<>();
    private GameTurnManager turnManager;

    public void setup() {
        playerOne = Player.builder().name("Archon A").build();
        playerTwo = Player.builder().name("Archon B").build();
        playerOne.drawCards(7);
        playerTwo.drawCards(6);
        activePlayer = playerOne; // Could be randomized
    }

    // Assign players (optional setter if needed)
    public void setPlayers(Player playerOne, Player playerTwo) {
        this.playerOne = playerOne;
        this.playerTwo = playerTwo;
    }

    public void run() {
        while (!isGameOver()) {
            turnManager.takeTurn(activePlayer);
        }
        log.info("Game over! Winner: {}", getWinner().getName());
    }

//    public void takeTurn(Player player) {
//        log.info("\nTurn {}: {}", turnCount + 1, player.getName());
//        player.forgeKey();
//        player.chooseHouse();
//        player.pickUpArchives();
//        player.playCards();
//        player.removeDestroyedCreatures();
//        player.readyCards();
//        player.drawCardsToSix();
//    }

    public void switchActivePlayer() {
        activePlayer = (activePlayer == playerOne) ? playerTwo : playerOne;
    }

    public boolean isGameOver() {
        return playerOne.getKeysForged() >= 3 || playerTwo.getKeysForged() >= 3;
    }

    public Player getWinner() {
        return playerOne.getKeysForged() >= 3 ? playerOne : playerTwo;
    }


    public void registerStaticEffect(PlayableCard card, ConstantAbility effect) {
        staticEffects.put(card, effect);
    }

    public void unregisterStaticEffect(PlayableCard card) {
        staticEffects.remove(card);
    }

    public void cleanup() {
        staticEffects.values().forEach(ConstantAbility::remove);
        staticEffects.clear();
    }

    public void trackRuleOfSix() {
        eventBus.subscribe(EventType.USED, event -> {
            UsedEventData usedData = event.get("usedEventData");
            PlayableCard card = usedData.getCard();
            String cardName = card.getName();
            int count = ruleOfSixCounter.getOrDefault(cardName, 0);
            ruleOfSixCounter.put(cardName, count + 1);
        });
    }

    public boolean canReap(CreatureCard creature, Player actingPlayer) {
        log.info("{} use {} to reap", actingPlayer.getName(), creature.getName());
        for (ConstantAbility effect : staticEffects.values()) {
            if (!effect.canReap(creature, actingPlayer, this)) {
                log.info("{} cannot reap due to {}'s constant ability.", creature.getName(), effect);
                return false;
            }
        }
        return true;
    }

    public boolean canFight(CreatureCard creature, Player actingPlayer) {
        log.info("{} use {} to fight", actingPlayer.getName(), creature.getName());
        for (ConstantAbility effect : staticEffects.values()) {
            if (!effect.canFight(creature, actingPlayer, this)) {
                log.info("{} cannot fight due to {}'s constant ability.", creature.getName(), effect);
                return false;
            }
        }
        return true;
    }

    public void setOwner(PlayableCard card, Player player) {
        cardOwnership.put(card, player);
    }

    public Player getOwnerOf(PlayableCard card) {
        return cardOwnership.get(card);
    }

    boolean canUseCard(PlayableCard card) {
        if (!(ruleOfSixCounter.getOrDefault(card.getName(), 0) < 6)) {
            log.info("{} cannot be used due to Rule of Six.", card.getName());
            return false;
        }
        ;
        return true;
    }

    public List<CreatureCard> getAllCreaturesInPlay() {
        List<CreatureCard> all = new ArrayList<>();
        if (playerOne != null) {
            all.addAll(playerOne.getPlayArea().getBattleline().getCreatures());
        }
        if (playerTwo != null) {
            all.addAll(playerTwo.getPlayArea().getBattleline().getCreatures());
        }
        return all;
    }

    public Player getOpponentOf(Player controller) {
        if (controller.equals(playerOne)) {
            return playerTwo;
        } else {
            return playerOne;
        }
    }

    public int getArmorBuffs(CreatureCard creature) {
        return staticEffects.entrySet().stream()
                .filter(entry -> !isTextBoxBlanked(entry.getKey()))
                .map(Map.Entry::getValue)
                .mapToInt(effect -> effect.armorBonus(creature, this))
                .sum();
    }

    public boolean isTextBoxBlanked(PlayableCard card) {
        return staticEffects.values().stream()
                .anyMatch(effect -> effect.blanksTextBox(card));
    }

    public List<CreatureCard> getAllCreatures() {
        var results = new ArrayList<CreatureCard>();
        var playerOneCreatures = playerOne.getPlayArea().getBattleline().getCreatures();
        var playerTwoCreatures = playerTwo.getPlayArea().getBattleline().getCreatures();
        results.addAll(playerOneCreatures);
        results.addAll(playerTwoCreatures);
        return results;
    }


    public void restrictHouseNextTurn(Player player, House house) {
        nextTurnHouseRestrictions.computeIfAbsent(player, k -> new HashSet<>()).add(house);
    }

    public Set<House> getRestrictedHouses(Player player) {
        return nextTurnHouseRestrictions.getOrDefault(player, Set.of());
    }

    public void clearHouseRestrictions(Player player) {
        nextTurnHouseRestrictions.remove(player);
    }
}
