package com.trunksbomb.minetriad.game;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Collections;

import com.trunksbomb.minetriad.card.CardRegistry;
import com.trunksbomb.minetriad.card.CardDefinition;
import com.trunksbomb.minetriad.card.CardStackData;
import com.trunksbomb.minetriad.item.CardItem;
import com.trunksbomb.minetriad.registry.TriadDataComponents;
import com.trunksbomb.minetriad.registry.TriadItems;

import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;

public final class DuelSessionManager {
    private static final Map<UUID, DuelSession> ACTIVE_DUELS = new ConcurrentHashMap<>();

    private DuelSessionManager() {
    }

    public static DuelSession get(Player player) {
        return ACTIVE_DUELS.get(player.getUUID());
    }

    public static DuelSession getAt(BlockPos tablePos) {
        return ACTIVE_DUELS.values().stream()
                .filter(session -> session.isAtTable(tablePos))
                .findFirst()
                .orElse(null);
    }

    public static DuelSession start(Player player, BlockPos tablePos) {
        return start(player, tablePos, false);
    }

    public static DuelSession start(Player player, BlockPos tablePos, boolean aiVsAi) {
        List<GameCard> playerHand = buildPlayerHand(player.getInventory());
        if (!aiVsAi && playerHand.size() < 5) {
            throw new IllegalStateException("A duel requires 5 Triad Cards in your inventory");
        }

        if (aiVsAi) {
            playerHand = buildRandomHand();
        }

        List<ResourceLocation> refundableCards = playerHand.stream()
                .map(card -> card.definition().id())
                .toList();
        DuelSession session = new DuelSession(
                player.getUUID(),
                player.getName().getString(),
                playerHand,
                aiVsAi ? buildRandomHand() : buildOpponentHand(),
                refundableCards,
                !aiVsAi && !player.getAbilities().instabuild,
                aiVsAi,
                tablePos);
        ACTIVE_DUELS.put(player.getUUID(), session);
        return session;
    }

    public static void end(Player player, boolean refundCards) {
        DuelSession session = ACTIVE_DUELS.remove(player.getUUID());
        if (session == null || !refundCards || !session.refundCardsOnEnd()) {
            return;
        }

        for (ResourceLocation cardId : session.playedPlayerCards()) {
            player.addItem(CardItem.createCardStack(cardId, TriadItems.TRIAD_CARD.get()));
        }
    }

    public static boolean refundRemainingIfActiveAt(Player player, BlockPos tablePos) {
        DuelSession session = ACTIVE_DUELS.remove(player.getUUID());
        if (session == null || !session.isAtTable(tablePos) || !session.refundCardsOnEnd()) {
            if (session != null && !session.isAtTable(tablePos)) {
                ACTIVE_DUELS.put(player.getUUID(), session);
            }
            return false;
        }

        for (ResourceLocation cardId : session.remainingPlayerCards()) {
            player.addItem(CardItem.createCardStack(cardId, TriadItems.TRIAD_CARD.get()));
        }
        return true;
    }

    public static boolean hasActiveAt(BlockPos tablePos) {
        return ACTIVE_DUELS.values().stream().anyMatch(session -> session.isAtTable(tablePos));
    }

    public static List<ResourceLocation> endAndCollectRemainingAt(BlockPos tablePos) {
        for (Map.Entry<UUID, DuelSession> entry : ACTIVE_DUELS.entrySet()) {
            DuelSession session = entry.getValue();
            if (session.isAtTable(tablePos)) {
                ACTIVE_DUELS.remove(entry.getKey());
                return session.remainingPlayerCards();
            }
        }
        return List.of();
    }

    private static List<GameCard> buildPlayerHand(Inventory inventory) {
        return inventory.items.stream()
                .filter(stack -> stack.is(TriadItems.TRIAD_CARD.get()))
                .map(stack -> stack.get(TriadDataComponents.CARD_DATA))
                .filter(data -> data != null && !CardStackData.EMPTY.equals(data))
                .map(CardStackData::cardId)
                .limit(5)
                .map(cardId -> new GameCard(CardRegistry.get(cardId)))
                .toList();
    }

    private static List<GameCard> buildOpponentHand() {
        return CardRegistry.all().stream()
                .sorted(Comparator.comparingInt((CardDefinition card) -> card.top() + card.right() + card.bottom() + card.left()).reversed())
                .limit(5)
                .map(GameCard::new)
                .toList();
    }

    private static List<GameCard> buildRandomHand() {
        List<CardDefinition> cards = new ArrayList<>(CardRegistry.all());
        Collections.shuffle(cards);
        return cards.stream()
                .limit(5)
                .map(GameCard::new)
                .toList();
    }
}
