/*
 * Copyright (c) 2026 盐灯水母
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

package com.remains_cave.dungeon.room;

import com.remains_cave.block.TombDoorCoreBlock;
import com.remains_cave.block.entity.TombDoorBlockEntity;
import com.remains_cave.entity.SkeletonArcherEntity;
import com.remains_cave.entity.SkeletonWarlockEntity;
import com.remains_cave.entity.SkeletonWarriorEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.*;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.saveddata.SavedData;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import com.remains_cave.entity.*;
public class RoomManager extends SavedData {
    private static final String DATA_NAME = "remains_cave_rooms";
    private final Map<String, RoomState> rooms = new ConcurrentHashMap<>();
    private final Map<String, List<BlockPos>> eliteSpawners = new ConcurrentHashMap<>();
    private final Map<String, List<BlockPos>> normalSpawners = new ConcurrentHashMap<>();
    private final Map<String, List<BlockPos>> roomAltars = new ConcurrentHashMap<>();
    private final Map<String, List<BlockPos>> roomDoors = new ConcurrentHashMap<>();

    private final Set<String> bossRooms = ConcurrentHashMap.newKeySet();   // 记录 BOSS 房间 ID
    private final Set<String> tombRooms = ConcurrentHashMap.newKeySet();   // 记录 Tomb 房间 ID
    public void markBossRoom(String roomId) {
        bossRooms.add(roomId);
        setDirty();
    }

    public List<LivingEntity> getSkeletonsInRoom(String roomId, ServerLevel level) {
        RoomState room = rooms.get(roomId);
        if (room == null || room.getProtectionArea() == null) {
            return Collections.emptyList();
        }
        AABB area = room.getProtectionArea();
        List<LivingEntity> result = new ArrayList<>();
        for (Entity e : level.getEntities().getAll()) {
            if (e instanceof SkeletonWarriorEntity ||
                    e instanceof SkeletonArcherEntity ||
                    e instanceof SkeletonWarlockEntity) {
                if (area.contains(e.getX(), e.getY(), e.getZ())) {
                    result.add((LivingEntity) e);
                }
            }
        }
        return result;
    }
    public Set<String> getBossRooms() { return Collections.unmodifiableSet(bossRooms); }
    public Set<String> getTombRooms() { return Collections.unmodifiableSet(tombRooms); }
    @Nullable
    public String getRoomIdAt(BlockPos pos) {
        for (Map.Entry<String, RoomState> entry : rooms.entrySet()) {
            AABB area = entry.getValue().getProtectionArea();
            if (area != null && area.contains(pos.getX(), pos.getY(), pos.getZ())) {
                return entry.getKey();
            }
        }
        return null;
    }
    // 标记 Tomb 房间
    public void markTombRoom(String roomId) {
        tombRooms.add(roomId);
        setDirty();
    }
    public void unlockAllRoomsExcept(String bossRoomId, Level level) {
        // 解锁除 bossRoom 和 tombRooms 外的所有房间
        for (Map.Entry<String, RoomState> entry : rooms.entrySet()) {
            String roomId = entry.getKey();
            if (roomId.equals(bossRoomId) || tombRooms.contains(roomId)) {
                continue;
            }
            RoomState state = entry.getValue();
            if (!state.isUnlocked()) {
                state.setUnlocked(true);
                setDirty();
            }
        }

        // 只对 bossRoom 内的玩家发送系统消息
        if (!level.isClientSide) {
            RoomState bossRoom = rooms.get(bossRoomId);
            if (bossRoom != null && bossRoom.getProtectionArea() != null) {
                AABB area = bossRoom.getProtectionArea();
                Component message = Component.translatable("message.remains_cave.rooms_unlocked");
                level.players().stream()
                        .filter(player -> area.contains(player.getX(), player.getY(), player.getZ()))
                        .forEach(player -> player.displayClientMessage(message, true)); // 改为 action bar
            }
        }
    }
    public void registerAltar(String roomId, BlockPos pos) {
        roomAltars.computeIfAbsent(roomId, k -> new CopyOnWriteArrayList<>()).add(pos);
        setDirty();
    }

    public Map<String, RoomState> getRooms() {
        return Collections.unmodifiableMap(rooms);
    }

    public List<BlockPos> getDoors(String roomId) {
        return roomDoors.getOrDefault(roomId, Collections.emptyList());
    }

    public List<BlockPos> getAltars(String roomId) {
        return roomAltars.getOrDefault(roomId, Collections.emptyList());
    }

    @Override
    public @NotNull CompoundTag save(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        // 保存精英刷怪笼
        ListTag eliteTag = new ListTag();
        for (Map.Entry<String, List<BlockPos>> entry : eliteSpawners.entrySet()) {
            CompoundTag roomTag = new CompoundTag();
            roomTag.putString("roomId", entry.getKey());
            ListTag posList = new ListTag();
            for (BlockPos pos : entry.getValue()) {
                posList.add(NbtUtils.writeBlockPos(pos));
            }
            roomTag.put("positions", posList);
            eliteTag.add(roomTag);
        }
        tag.put("eliteSpawners", eliteTag);

        // 保存普通刷怪笼
        ListTag normalTag = new ListTag();
        for (Map.Entry<String, List<BlockPos>> entry : normalSpawners.entrySet()) {
            CompoundTag roomTag = new CompoundTag();
            roomTag.putString("roomId", entry.getKey());
            ListTag posList = new ListTag();
            for (BlockPos pos : entry.getValue()) {
                posList.add(NbtUtils.writeBlockPos(pos));
            }
            roomTag.put("positions", posList);
            normalTag.add(roomTag);
        }
        tag.put("normalSpawners", normalTag);

        // 保存房间状态
        ListTag roomsList = new ListTag();
// RoomManager.save() 中保存 rooms 列表的部分
        for (RoomState room : rooms.values()) {
            CompoundTag roomTag = new CompoundTag();
            roomTag.putString("id", room.getRoomId());
            roomTag.putBoolean("bossActive", room.isBossActive());
            roomTag.putBoolean("unlocked", room.isUnlocked()); // 新增

            //保存步骤标识和关联信息
            roomTag.putString("stepId", room.getStepId() != null ? room.getStepId() : "");
            roomTag.putString("parentStaircaseId", room.getParentStaircaseId() != null ? room.getParentStaircaseId() : "");
            roomTag.putBoolean("emblemActive", room.isEmblemActive());

            ListTag doorsList = new ListTag();
            for (BlockPos pos : room.getDoors()) {
                doorsList.add(NbtUtils.writeBlockPos(pos));
            }
            roomTag.put("doors", doorsList);

            // 保存保护区域
            AABB area = room.getProtectionArea();
            if (area != null) {
                CompoundTag areaTag = new CompoundTag();
                areaTag.putDouble("minX", area.minX);
                areaTag.putDouble("minY", area.minY);
                areaTag.putDouble("minZ", area.minZ);
                areaTag.putDouble("maxX", area.maxX);
                areaTag.putDouble("maxY", area.maxY);
                areaTag.putDouble("maxZ", area.maxZ);
                roomTag.put("protectionArea", areaTag);
            }

            roomsList.add(roomTag);
        }
        tag.put("rooms", roomsList);

        // 保存祭坛
        ListTag altarTag = new ListTag();
        for (Map.Entry<String, List<BlockPos>> entry : roomAltars.entrySet()) {
            CompoundTag roomTag = new CompoundTag();
            roomTag.putString("roomId", entry.getKey());
            ListTag posList = new ListTag();
            for (BlockPos pos : entry.getValue()) {
                posList.add(NbtUtils.writeBlockPos(pos));
            }
            roomTag.put("positions", posList);
            altarTag.add(roomTag);
        }
        tag.put("roomAltars", altarTag);

        // 保存独立门列表
        ListTag doorTag = new ListTag();
        for (Map.Entry<String, List<BlockPos>> entry : roomDoors.entrySet()) {
            CompoundTag roomTag = new CompoundTag();
            roomTag.putString("roomId", entry.getKey());
            ListTag posList = new ListTag();
            for (BlockPos pos : entry.getValue()) {
                posList.add(NbtUtils.writeBlockPos(pos));
            }
            roomTag.put("positions", posList);
            doorTag.add(roomTag);
        }
        tag.put("roomDoors", doorTag);

        ListTag bossList = new ListTag();
        for (String id : bossRooms) {
            bossList.add(StringTag.valueOf(id));
        }
        tag.put("bossRooms", bossList);

        // 保存 tombRooms
        ListTag tombList = new ListTag();
        for (String id : tombRooms) {
            tombList.add(StringTag.valueOf(id));
        }
        tag.put("tombRooms", tombList);

        return tag;
    }

    public static RoomManager load(CompoundTag tag, HolderLookup.Provider registries) {
        RoomManager manager = new RoomManager();

        // 加载精英刷怪笼
        if (tag.contains("eliteSpawners")) {
            ListTag eliteTag = tag.getList("eliteSpawners", Tag.TAG_COMPOUND);
            for (int i = 0; i < eliteTag.size(); i++) {
                CompoundTag roomTag = eliteTag.getCompound(i);
                String roomId = roomTag.getString("roomId");
                ListTag posList = roomTag.getList("positions", Tag.TAG_INT_ARRAY);
                List<BlockPos> positions = new ArrayList<>();
                for (int j = 0; j < posList.size(); j++) {
                    int[] posArray = posList.getIntArray(j);
                    if (posArray.length == 3) {
                        positions.add(new BlockPos(posArray[0], posArray[1], posArray[2]));
                    }
                }
                manager.eliteSpawners.put(roomId, new CopyOnWriteArrayList<>(positions));
            }
        }

        // 加载普通刷怪笼
        if (tag.contains("normalSpawners")) {
            ListTag normalTag = tag.getList("normalSpawners", Tag.TAG_COMPOUND);
            for (int i = 0; i < normalTag.size(); i++) {
                CompoundTag roomTag = normalTag.getCompound(i);
                String roomId = roomTag.getString("roomId");
                ListTag posList = roomTag.getList("positions", Tag.TAG_INT_ARRAY);
                List<BlockPos> positions = new ArrayList<>();
                for (int j = 0; j < posList.size(); j++) {
                    int[] posArray = posList.getIntArray(j);
                    if (posArray.length == 3) {
                        positions.add(new BlockPos(posArray[0], posArray[1], posArray[2]));
                    }
                }
                manager.normalSpawners.put(roomId, new CopyOnWriteArrayList<>(positions));
            }
        }

        // 加载祭坛
        if (tag.contains("roomAltars")) {
            ListTag altarTag = tag.getList("roomAltars", Tag.TAG_COMPOUND);
            for (int i = 0; i < altarTag.size(); i++) {
                CompoundTag roomTag = altarTag.getCompound(i);
                String roomId = roomTag.getString("roomId");
                ListTag posList = roomTag.getList("positions", Tag.TAG_INT_ARRAY);
                List<BlockPos> positions = new ArrayList<>();
                for (int j = 0; j < posList.size(); j++) {
                    int[] posArray = posList.getIntArray(j);
                    if (posArray.length == 3) {
                        positions.add(new BlockPos(posArray[0], posArray[1], posArray[2]));
                    }
                }
                manager.roomAltars.put(roomId, new CopyOnWriteArrayList<>(positions));
            }
        }

        // 加载房间状态
// RoomManager.load() 中加载房间状态的循环
        ListTag roomsList = tag.getList("rooms", Tag.TAG_COMPOUND);
        for (int i = 0; i < roomsList.size(); i++) {
            CompoundTag roomTag = roomsList.getCompound(i);
            String id = roomTag.getString("id");
            boolean bossActive = roomTag.getBoolean("bossActive");
            RoomState room = new RoomState(id);
            room.setBossActive(bossActive);
            if (roomTag.contains("unlocked")) {
                room.setUnlocked(roomTag.getBoolean("unlocked"));
            }
            if (roomTag.contains("stepId")) {
                room.setStepId(roomTag.getString("stepId"));
            }
            if (roomTag.contains("parentStaircaseId")) {
                room.setParentStaircaseId(roomTag.getString("parentStaircaseId"));
            }
            if (roomTag.contains("emblemActive")) {
                room.setEmblemActive(roomTag.getBoolean("emblemActive"));
            }
            // 读取保护区域
            if (roomTag.contains("protectionArea")) {
                CompoundTag areaTag = roomTag.getCompound("protectionArea");
                double minX = areaTag.getDouble("minX");
                double minY = areaTag.getDouble("minY");
                double minZ = areaTag.getDouble("minZ");
                double maxX = areaTag.getDouble("maxX");
                double maxY = areaTag.getDouble("maxY");
                double maxZ = areaTag.getDouble("maxZ");
                room.setProtectionArea(new AABB(minX, minY, minZ, maxX, maxY, maxZ));
            }

            ListTag doorsList = roomTag.getList("doors", Tag.TAG_INT_ARRAY);
            for (int j = 0; j < doorsList.size(); j++) {
                int[] posArray = doorsList.getIntArray(j);
                if (posArray.length == 3) {
                    room.addDoor(new BlockPos(posArray[0], posArray[1], posArray[2]));
                }
            }
            manager.rooms.put(id, room);
        }

        // 加载独立门列表
        if (tag.contains("roomDoors")) {
            ListTag doorTag = tag.getList("roomDoors", Tag.TAG_COMPOUND);
            for (int i = 0; i < doorTag.size(); i++) {
                CompoundTag roomTag = doorTag.getCompound(i);
                String roomId = roomTag.getString("roomId");
                ListTag posList = roomTag.getList("positions", Tag.TAG_INT_ARRAY);
                List<BlockPos> positions = new ArrayList<>();
                for (int j = 0; j < posList.size(); j++) {
                    int[] posArray = posList.getIntArray(j);
                    if (posArray.length == 3) {
                        positions.add(new BlockPos(posArray[0], posArray[1], posArray[2]));
                    }
                }
                manager.roomDoors.put(roomId, new CopyOnWriteArrayList<>(positions));
            }
        }

        // 合并门列表到房间状态
        for (Map.Entry<String, List<BlockPos>> entry : manager.roomDoors.entrySet()) {
            String roomId = entry.getKey();
            List<BlockPos> doors = entry.getValue();
            RoomState room = manager.rooms.get(roomId);
            if (room != null) {
                for (BlockPos pos : doors) {
                    if (!room.getDoors().contains(pos)) {
                        room.addDoor(pos);
                    }
                }
            } else {
                room = new RoomState(roomId);
                for (BlockPos pos : doors) {
                    room.addDoor(pos);
                }
                manager.rooms.put(roomId, room);
            }
        }

        // 确保 roomDoors 包含所有门（从 RoomState 补充）
        for (Map.Entry<String, RoomState> entry : manager.rooms.entrySet()) {
            String roomId = entry.getKey();
            RoomState state = entry.getValue();
            if (!manager.roomDoors.containsKey(roomId) && !state.getDoors().isEmpty()) {
                manager.roomDoors.put(roomId, new ArrayList<>(state.getDoors()));
            }
        }
        // 加载 bossRooms
        if (tag.contains("bossRooms")) {
            ListTag bossList = tag.getList("bossRooms", Tag.TAG_STRING);
            for (int i = 0; i < bossList.size(); i++) {
                manager.bossRooms.add(bossList.getString(i));
            }
        }

        // 加载 tombRooms
        if (tag.contains("tombRooms")) {
            ListTag tombList = tag.getList("tombRooms", Tag.TAG_STRING);
            for (int i = 0; i < tombList.size(); i++) {
                manager.tombRooms.add(tombList.getString(i));
            }
        }
        return manager;
    }

    public Map<String, List<BlockPos>> getRoomDoors() {
        return roomDoors;
    }

    public static RoomManager get(Level level) {
        if (!(level instanceof ServerLevel serverLevel)) {
            throw new IllegalStateException("RoomManager can only be accessed on server side");
        }
        DimensionDataStorage storage = serverLevel.getDataStorage();
        return storage.computeIfAbsent(
                new SavedData.Factory<>(RoomManager::new, RoomManager::load),
                DATA_NAME
        );
    }

    public RoomState getOrCreateRoom(String roomId) {
        return rooms.computeIfAbsent(roomId, RoomState::new);
    }

    public void registerDoor(String roomId, BlockPos doorPos) {
        List<BlockPos> list = roomDoors.computeIfAbsent(roomId, k -> new CopyOnWriteArrayList<>());
        if (!list.contains(doorPos)) {
            list.add(doorPos);
        }
        RoomState room = getOrCreateRoom(roomId);
        if (!room.getDoors().contains(doorPos)) {
            room.addDoor(doorPos);
        }
        setDirty();
    }

    public void unregisterDoor(String roomId, BlockPos doorPos) {
        List<BlockPos> list = roomDoors.get(roomId);
        if (list != null && list.remove(doorPos)) {
            if (list.isEmpty()) {
                roomDoors.remove(roomId);
            }
        }
        RoomState room = rooms.get(roomId);
        if (room != null) {
            room.removeDoor(doorPos);
            if (room.getDoors().isEmpty()) {
                rooms.remove(roomId);
            }
        }
        setDirty();
    }

    public void activateBoss(String roomId, ServerLevel level) {
        List<BlockPos> doors = roomDoors.get(roomId);
        RoomState roomState = rooms.get(roomId);
        if ((doors == null || doors.isEmpty()) && roomState != null && !roomState.getDoors().isEmpty()) {
            doors = new CopyOnWriteArrayList<>(roomState.getDoors());
            roomDoors.put(roomId, doors);
        }
        if (doors == null || doors.isEmpty()) {
            return;
        }
        for (BlockPos pos : doors) {
            updateDoorState(level, pos, true);
        }
        if (roomState != null && !roomState.isBossActive()) {
            roomState.setBossActive(true);
            setDirty();
        }
    }

    public void deactivateBoss(String roomId, ServerLevel level) {
        List<BlockPos> doors = roomDoors.get(roomId);
        RoomState roomState = rooms.get(roomId);
        if ((doors == null || doors.isEmpty()) && roomState != null && !roomState.getDoors().isEmpty()) {
            doors = new CopyOnWriteArrayList<>(roomState.getDoors());
            roomDoors.put(roomId, doors);
        }
        if (doors == null || doors.isEmpty()) {
            return;
        }
        for (BlockPos pos : doors) {
            updateDoorState(level, pos, false);
        }
        if (roomState != null && roomState.isBossActive()) {
            roomState.setBossActive(false);
            setDirty();
        }
    }

    public void registerEliteSpawner(String roomId, BlockPos pos) {
        eliteSpawners.computeIfAbsent(roomId, k -> new CopyOnWriteArrayList<>()).add(pos);
        setDirty();
    }

    public void registerNormalSpawner(String roomId, BlockPos pos) {
        normalSpawners.computeIfAbsent(roomId, k -> new CopyOnWriteArrayList<>()).add(pos);
        setDirty();
    }

    public void unregisterEliteSpawner(String roomId, BlockPos pos) {
        List<BlockPos> list = eliteSpawners.get(roomId);
        if (list != null) {
            list.remove(pos);
            if (list.isEmpty()) {
                eliteSpawners.remove(roomId);
            }
            setDirty();
        }
    }

    public void unregisterNormalSpawner(String roomId, BlockPos pos) {
        List<BlockPos> list = normalSpawners.get(roomId);
        if (list != null) {
            list.remove(pos);
            if (list.isEmpty()) {
                normalSpawners.remove(roomId);
            }
            setDirty();
        }
    }

    public List<BlockPos> getEliteSpawners(String roomId) {
        return eliteSpawners.getOrDefault(roomId, Collections.emptyList());
    }

    public List<BlockPos> getNormalSpawners(String roomId) {
        return normalSpawners.getOrDefault(roomId, Collections.emptyList());
    }

    public void updateDoorState(ServerLevel level, BlockPos pos, boolean lock) {
        var state = level.getBlockState(pos);
        if (state.getBlock() instanceof TombDoorCoreBlock) {
            level.setBlock(pos, state.setValue(TombDoorCoreBlock.LOCKED, lock), 3);
            if (level.getBlockEntity(pos) instanceof TombDoorBlockEntity doorBE) {
                boolean newOpen = !lock;
                doorBE.setOpen(newOpen);
            }
        }
    }
}