/*
 * 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.tomb;

import com.remains_cave.block.TombDoorCoreBlock;
import com.remains_cave.block.entity.ChallengeAltarBlockEntity;
import com.remains_cave.dungeon.room.RoomManager;
import com.remains_cave.dungeon.room.RoomState;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.saveddata.SavedData;
import net.minecraft.world.level.storage.DimensionDataStorage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ChallengeManager extends SavedData {
    private static final String DATA_NAME = "remains_cave_challenges";
    private final Map<String, RoomChallenge> challenges = new ConcurrentHashMap<>();
    private boolean initialized = false;   // 实例标志，记录是否已初始化

    public static ChallengeManager get(Level level) {
        if (!(level instanceof ServerLevel serverLevel))
            throw new IllegalStateException("Only server side");
        DimensionDataStorage storage = serverLevel.getDataStorage();
        ChallengeManager manager = storage.computeIfAbsent(
                new SavedData.Factory<>(ChallengeManager::new, ChallengeManager::load),
                DATA_NAME
        );
        if (!manager.initialized) {
            manager.initialized = true;
            // 清除所有挑战（异常崩溃残留）
            manager.clearAll();
            // 修复门和祭坛状态（基于清空后的挑战数据）
            manager.repairDoorStates(serverLevel);
            manager.repairAltarStates(serverLevel);
        } else {
            // 常规运行时修复（可选，保证状态同步）
            manager.repairDoorStates(serverLevel);
            manager.repairAltarStates(serverLevel);
        }
        return manager;
    }
    public boolean forceCleanup(ServerLevel level) {
        System.out.println("forceCleanup called for " + level.dimension().location());
        // 只清除带 RoomId 的实体，不清除挑战数据
        List<Entity> toRemove = new ArrayList<>();
        for (Entity entity : level.getEntities().getAll()) {
            if (entity.getPersistentData().contains("RoomId")) {
                toRemove.add(entity);
            }
        }
        for (Entity entity : toRemove) {
            entity.discard();
        }
        boolean cleaned = !toRemove.isEmpty();
        System.out.println("Cleaned " + toRemove.size() + " challenge entities in " + level.dimension().location());

        // 注意：不要清空 challenges 映射！挑战数据是持久化状态，可能包含正在进行的挑战。
        // 只根据现有 challenges 修复门和祭坛状态
        repairDoorStates(level);
        repairAltarStates(level);
        return cleaned;
    }
    private void repairDoorStates(ServerLevel level) {
        RoomManager roomManager = RoomManager.get(level);
        // 使用副本避免并发修改异常
        Map<String, RoomState> roomsCopy = new HashMap<>(roomManager.getRooms());
        for (Map.Entry<String, RoomState> entry : roomsCopy.entrySet()) {
            String roomId = entry.getKey();
            // 跳过没有祭坛的房间
            if (roomManager.getAltars(roomId).isEmpty()) {
                continue;
            }
            RoomState state = entry.getValue();
            boolean shouldLock = this.challenges.containsKey(roomId);
            List<BlockPos> doors = roomManager.getDoors(roomId);
            if ((doors == null || doors.isEmpty()) && state != null && !state.getDoors().isEmpty()) {
                doors = new ArrayList<>(state.getDoors());
                roomManager.getRoomDoors().put(roomId, doors);
            }
            if (doors == null || doors.isEmpty()) {
                continue;
            }
            for (BlockPos pos : doors) {
                BlockState blockState = level.getBlockState(pos);
                if (blockState.getBlock() instanceof TombDoorCoreBlock) {
                    boolean isLocked = blockState.getValue(TombDoorCoreBlock.LOCKED);
                    if (isLocked != shouldLock) {
                        roomManager.updateDoorState(level, pos, shouldLock);
                    }
                }
            }
            if (state != null && state.isBossActive() != shouldLock) {
                state.setBossActive(shouldLock);
                roomManager.setDirty();
            }
        }
    }

    private void repairAltarStates(ServerLevel level) {
        RoomManager roomManager = RoomManager.get(level);
        // 使用副本避免并发修改异常
        Map<String, RoomState> roomsCopy = new HashMap<>(roomManager.getRooms());
        for (Map.Entry<String, RoomState> entry : roomsCopy.entrySet()) {
            String roomId = entry.getKey();
            RoomChallenge challenge = this.challenges.get(roomId);
            boolean hasActiveChallenge = challenge != null && challenge.isActive();
            List<BlockPos> altars = roomManager.getAltars(roomId);
            if (altars == null || altars.isEmpty()) continue;
            for (BlockPos pos : altars) {
                if (level.getBlockEntity(pos) instanceof ChallengeAltarBlockEntity altar) {
                    if (!hasActiveChallenge && altar.isChallengeActive()) {
                        altar.setChallengeActive(false, ChallengeType.NONE, null);
                    }
                }
            }
        }
    }

    public static ChallengeManager load(CompoundTag tag, HolderLookup.Provider registries) {
        ChallengeManager manager = new ChallengeManager();
        ListTag list = tag.getList("challenges", Tag.TAG_COMPOUND);
        for (int i = 0; i < list.size(); i++) {
            CompoundTag ct = list.getCompound(i);
            String roomId = ct.getString("roomId");
            RoomChallenge challenge = new RoomChallenge();
            challenge.loadFromNbt(ct, registries);
            manager.challenges.put(roomId, challenge);
        }
        return manager;
    }

    public Map<String, RoomChallenge> getChallenges() {
        return Collections.unmodifiableMap(challenges);
    }

    public void clearAll() {
        challenges.clear();
        setDirty();
    }

    @Override
    public @NotNull CompoundTag save(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        ListTag list = new ListTag();
        for (Map.Entry<String, RoomChallenge> e : challenges.entrySet()) {
            CompoundTag ct = new CompoundTag();
            ct.putString("roomId", e.getKey());
            e.getValue().save(ct);
            list.add(ct);
        }
        tag.put("challenges", list);
        return tag;
    }

    @Nullable
    public RoomChallenge getChallenge(String roomId) {
        return challenges.get(roomId);
    }

    public boolean isChallengeActive(String roomId) {
        RoomChallenge c = challenges.get(roomId);
        return c != null && c.isActive();
    }

    public void startChallenge(String roomId, BlockPos altarPos, ChallengeType type, ServerLevel level) {
        if (isChallengeActive(roomId)) return;

        RoomManager roomManager = RoomManager.get(level);
        List<BlockPos> altars = roomManager.getAltars(roomId);
        for (BlockPos pos : altars) {
            if (level.getBlockEntity(pos) instanceof ChallengeAltarBlockEntity altar) {
                altar.setChallengeActive(true, type, altarPos);
            }
        }

        RoomChallenge challenge = new RoomChallenge(roomId, altarPos, type);
        challenges.put(roomId, challenge);
        setDirty();
    }

    public void removeChallenge(String roomId) {
        challenges.remove(roomId);
        setDirty();
    }

    public void onMobDeath(LivingEntity entity) {
        CompoundTag data = entity.getPersistentData();
        if (!data.contains("RoomId")) return;
        String roomId = data.getString("RoomId");
        RoomChallenge challenge = challenges.get(roomId);
        if (challenge != null && challenge.isActive()) {
            boolean isElite = data.getBoolean("IsElite");
            challenge.onMobDeath(isElite);
            setDirty();
        }
    }

    public void tick(ServerLevel level) {
        for (RoomChallenge challenge : challenges.values()) {
            challenge.tick(level);
        }
    }
}