package com.tiedup.remake.state;

import com.tiedup.remake.core.TiedUpMod;
import java.util.*;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.saveddata.SavedData;

/**
 * Persistent storage for social command data.
 *
 * Stores:
 * - Block lists (which players have blocked which other players)
 * - Talk area settings (local chat distance per player)
 *
 */
public class SocialData extends SavedData {

    private static final String DATA_NAME = TiedUpMod.MOD_ID + "_social";

    // Block lists per player UUID
    private final Map<UUID, Set<UUID>> blockedPlayers = new HashMap<>();

    // Talk area settings per player UUID
    private final Map<UUID, Integer> talkAreas = new HashMap<>();

    public SocialData() {}

    /**
     * Load from NBT.
     */
    public static SocialData load(CompoundTag tag) {
        SocialData data = new SocialData();

        // Load blocked players
        if (tag.contains("blocked")) {
            ListTag blockedList = tag.getList("blocked", Tag.TAG_COMPOUND);
            for (int i = 0; i < blockedList.size(); i++) {
                CompoundTag entry = blockedList.getCompound(i);
                UUID blocker = entry.getUUID("blocker");
                Set<UUID> blocked = new HashSet<>();

                ListTag blockedUUIDs = entry.getList(
                    "blocked_uuids",
                    Tag.TAG_COMPOUND
                );
                for (int j = 0; j < blockedUUIDs.size(); j++) {
                    CompoundTag uuidTag = blockedUUIDs.getCompound(j);
                    blocked.add(uuidTag.getUUID("uuid"));
                }

                if (!blocked.isEmpty()) {
                    data.blockedPlayers.put(blocker, blocked);
                }
            }
        }

        // Load talk areas
        if (tag.contains("talk_areas")) {
            ListTag talkList = tag.getList("talk_areas", Tag.TAG_COMPOUND);
            for (int i = 0; i < talkList.size(); i++) {
                CompoundTag entry = talkList.getCompound(i);
                UUID player = entry.getUUID("player");
                int distance = entry.getInt("distance");
                data.talkAreas.put(player, distance);
            }
        }

        TiedUpMod.LOGGER.debug(
            "[SocialData] Loaded {} block lists, {} talk areas",
            data.blockedPlayers.size(),
            data.talkAreas.size()
        );

        return data;
    }

    @Override
    public CompoundTag save(CompoundTag tag) {
        // Save blocked players
        ListTag blockedList = new ListTag();
        for (Map.Entry<UUID, Set<UUID>> entry : blockedPlayers.entrySet()) {
            if (entry.getValue().isEmpty()) continue;

            CompoundTag blockerTag = new CompoundTag();
            blockerTag.putUUID("blocker", entry.getKey());

            ListTag blockedUUIDs = new ListTag();
            for (UUID blocked : entry.getValue()) {
                CompoundTag uuidTag = new CompoundTag();
                uuidTag.putUUID("uuid", blocked);
                blockedUUIDs.add(uuidTag);
            }
            blockerTag.put("blocked_uuids", blockedUUIDs);
            blockedList.add(blockerTag);
        }
        tag.put("blocked", blockedList);

        // Save talk areas
        ListTag talkList = new ListTag();
        for (Map.Entry<UUID, Integer> entry : talkAreas.entrySet()) {
            CompoundTag talkTag = new CompoundTag();
            talkTag.putUUID("player", entry.getKey());
            talkTag.putInt("distance", entry.getValue());
            talkList.add(talkTag);
        }
        tag.put("talk_areas", talkList);

        return tag;
    }

    // ==================== Block List Methods ====================

    /**
     * Add a player to another player's block list.
     */
    public void addBlock(UUID blocker, UUID blocked) {
        blockedPlayers
            .computeIfAbsent(blocker, k -> new HashSet<>())
            .add(blocked);
        setDirty();
    }

    /**
     * Remove a player from another player's block list.
     */
    public void removeBlock(UUID blocker, UUID blocked) {
        Set<UUID> set = blockedPlayers.get(blocker);
        if (set != null) {
            set.remove(blocked);
            if (set.isEmpty()) {
                blockedPlayers.remove(blocker);
            }
            setDirty();
        }
    }

    /**
     * Check if a player has blocked another.
     */
    public boolean isBlocked(UUID blocker, UUID blocked) {
        Set<UUID> set = blockedPlayers.get(blocker);
        return set != null && set.contains(blocked);
    }

    /**
     * Get the set of players blocked by a player.
     */
    public Set<UUID> getBlockedPlayers(UUID blocker) {
        return blockedPlayers.getOrDefault(blocker, Collections.emptySet());
    }

    // ==================== Talk Area Methods ====================

    /**
     * Set a player's talk area distance.
     */
    public void setTalkArea(UUID player, int distance) {
        if (distance <= 0) {
            talkAreas.remove(player);
        } else {
            talkAreas.put(player, distance);
        }
        setDirty();
    }

    /**
     * Get a player's talk area distance (0 = global chat).
     */
    public int getTalkArea(UUID player) {
        return talkAreas.getOrDefault(player, 0);
    }

    // ==================== Static Access ====================

    /**
     * Get the SocialData for a server level.
     */
    public static SocialData get(ServerLevel level) {
        return level
            .getDataStorage()
            .computeIfAbsent(SocialData::load, SocialData::new, DATA_NAME);
    }
}
