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

import com.remains_cave.block.TrialVaultBlock;
import com.remains_cave.block.TrialVaultType;
import com.remains_cave.block.ModBlocks;
import com.remains_cave.item.ModItems;
import net.minecraft.core.*;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.EnchantedBookItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.ItemEnchantments;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector3f;
import org.yaml.snakeyaml.Yaml;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import java.io.InputStream;
import java.util.*;

public class TrialVaultBlockEntity extends BlockEntity {

    private static final String TAG_LOOT_TABLE = "LootTable";
    private static final String TAG_OPENED = "Opened";
    private ResourceLocation lootTable;
    private boolean opened;
    private int glowTicks;
    private int checkCooldown;
    private int activeToEjectDelay = 0;      // 从 inactive 到 ejecting 的延迟
    private int ejectStartDelay = 0;          // 从 ejecting 到开始喷出的延迟
    private int closeDelay = 0;               // 关闭前的延迟
    private final TrialVaultType type;
    private int particleCooldown = 0;
    // 物品弹出相关
    private final List<ItemStack> pendingItems = new ArrayList<>();
    private int ejectDelay = 0;
    private static final int ACTIVE_TO_EJECT_DELAY_TICKS = 10;   // 10 tick
    private static final int EJECT_START_DELAY_TICKS = 20;       // 20 tick
    private static final int ITEM_EJECT_DELAY_TICKS = 20;        // 物品间隔 20 tick
    private static final int CLOSE_DELAY_TICKS = 20;             // 关闭延迟 20 tick

    private int cooldownTicks = 0;
    private static final int COOLDOWN_TICKS = 7200 * 20;

    public TrialVaultBlockEntity(BlockPos pos, BlockState state, TrialVaultType type) {
        super(ModBlocks.TRIAL_VAULT_BLOCK_ENTITY.get(), pos, state);
        this.type = type;
        String modId = "remains_cave";
        if (type == TrialVaultType.ROOM) {
            this.lootTable = ResourceLocation.fromNamespaceAndPath(modId, "chests/random_reward");
        } else {
            this.lootTable = ResourceLocation.fromNamespaceAndPath(modId, "chests/boss_reward");
        }
    }

    private void spawnActivateParticles() {
        if (!(level instanceof ServerLevel serverLevel)) return;
        Direction facing = getBlockState().getValue(TrialVaultBlock.FACING);
        Vec3 mouthPos = Vec3.atCenterOf(worldPosition).add(facing.getStepX() * 0.5, 0.5, facing.getStepZ() * 0.5);

        // 已去除从玩家飘向宝库的火焰粒子

        // 宝库内部火焰粒子（激活瞬间生成较多，位置略低）
        for (int i = 0; i < 8; i++) {
            double x = worldPosition.getX() + 0.5 + (level.random.nextDouble() - 0.5) * 0.8;
            double y = worldPosition.getY() + 0.5 + level.random.nextDouble() * 0.8; // 高度 0.5~1.3
            double z = worldPosition.getZ() + 0.5 + (level.random.nextDouble() - 0.5) * 0.8;
            serverLevel.sendParticles(ParticleTypes.FLAME, x, y, z, 1, 0, 0.05, 0, 0);
        }
    }
    private void addActiveParticles() {
        if (!(level instanceof ServerLevel serverLevel)) return;

        Direction facing = getBlockState().getValue(TrialVaultBlock.FACING);
        Vec3 mouthPos = Vec3.atCenterOf(worldPosition).add(facing.getStepX() * 0.5, 0.5, facing.getStepZ() * 0.5);

        // 红色粉尘的颜色值 (#e55050)
        Vector3f redColor = new Vector3f(0.898f, 0.314f, 0.314f);

        // 获取当前状态
        BlockState currentState = getBlockState();
        boolean isActiveOrEjecting = currentState.getValue(TrialVaultBlock.VAULT_STATE) == TrialVaultBlock.VaultState.ACTIVE ||
                currentState.getValue(TrialVaultBlock.VAULT_STATE) == TrialVaultBlock.VaultState.EJECTING;

        // 只有 ACTIVE/EJECTING 才生成火焰粒子
        if (isActiveOrEjecting) {
            int flameCount = 1 + level.random.nextInt(2);
            for (int i = 0; i < flameCount; i++) {
                double x = worldPosition.getX() + 0.5 + (level.random.nextDouble() - 0.5) * 0.8;
                double y = worldPosition.getY() + 0.3 + level.random.nextDouble() * 0.5; // 高度 0.3~0.8
                double z = worldPosition.getZ() + 0.5 + (level.random.nextDouble() - 0.5) * 0.8;
                serverLevel.sendParticles(ParticleTypes.FLAME, x, y, z, 1, 0, 0.05, 0, 0);
            }
        }

        // 所有状态（INACTIVE/ACTIVE/EJECTING）都生成粉尘粒子
        int dustCount = 1 + level.random.nextInt(2);
        for (int i = 0; i < dustCount; i++) {
            double x = worldPosition.getX() + 0.5 + (level.random.nextDouble() - 0.5);
            double y = worldPosition.getY() + 0.2 + level.random.nextDouble() * 0.8; // 高度 0.2~1.0
            double z = worldPosition.getZ() + 0.5 + (level.random.nextDouble() - 0.5);
            double vx = (level.random.nextDouble() - 0.5) * 0.05;
            double vy = level.random.nextDouble() * 0.1;
            double vz = (level.random.nextDouble() - 0.5) * 0.05;
            serverLevel.sendParticles(new DustParticleOptions(redColor, 0.8f),
                    x, y, z, 1, vx, vy, vz, 0);
        }
    }
    public TrialVaultBlockEntity(BlockPos pos, BlockState state) {
        this(pos, state, getTypeFromBlock(state));
    }

    private static TrialVaultType getTypeFromBlock(BlockState state) {
        if (state.getBlock() instanceof TrialVaultBlock vaultBlock) {
            return vaultBlock.getType();
        }
        return TrialVaultType.ROOM;
    }

    @Override
    protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        super.saveAdditional(tag, registries);
        if (lootTable != null) tag.putString(TAG_LOOT_TABLE, lootTable.toString());
        tag.putBoolean(TAG_OPENED, opened);
    }

    @Override
    public @NotNull CompoundTag getUpdateTag(HolderLookup.@NotNull Provider registries) {
        CompoundTag tag = super.getUpdateTag(registries);
        tag.putInt("GlowTicks", glowTicks);
        tag.putBoolean(TAG_OPENED, opened);
        if (lootTable != null) tag.putString(TAG_LOOT_TABLE, lootTable.toString());
        return tag;
    }

    @Override
    public void handleUpdateTag(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        super.handleUpdateTag(tag, registries);
        this.glowTicks = tag.getInt("GlowTicks");
        this.opened = tag.getBoolean(TAG_OPENED);
        if (tag.contains(TAG_LOOT_TABLE)) {
            this.lootTable = ResourceLocation.parse(tag.getString(TAG_LOOT_TABLE));
        }
    }

    @Override
    protected void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        super.loadAdditional(tag, registries);
        if (tag.contains(TAG_LOOT_TABLE)) lootTable = ResourceLocation.parse(tag.getString(TAG_LOOT_TABLE));
        opened = tag.getBoolean(TAG_OPENED);
        this.glowTicks = tag.getInt("GlowTicks");
    }

    public static void serverTick(Level level, BlockPos pos, BlockState state, TrialVaultBlockEntity vault) {
        if (level.isClientSide) return;
        if (vault.cooldownTicks > 0) {
            vault.cooldownTicks--;
            if (vault.cooldownTicks == 0) {
                vault.opened = false;      // 冷却结束，允许再次开启
                vault.setChanged();
                vault.syncToClient();
            }
        }

        // ===== 每 tick 必须执行的状态机 =====
        // 阶段1：inactive → ejecting 延迟
        if (vault.activeToEjectDelay > 0) {
            vault.activeToEjectDelay--;
            if (vault.activeToEjectDelay == 0) {
                vault.setEjectingState();
                vault.ejectStartDelay = EJECT_START_DELAY_TICKS;
            }
        }

        // 阶段2：ejecting → 开始弹出延迟
        if (vault.ejectStartDelay > 0) {
            vault.ejectStartDelay--;
            if (vault.ejectStartDelay == 0) {
                vault.startEjecting();
            }
        }

        // 物品弹出延迟
        if (!vault.pendingItems.isEmpty() && vault.ejectDelay <= 0) {
            ItemStack stack = vault.pendingItems.removeFirst();
            vault.spawnItem(stack);
            level.playSound(null, pos, SoundEvents.VAULT_EJECT_ITEM, SoundSource.BLOCKS, 1.0F, 1.0F);
            vault.ejectDelay = ITEM_EJECT_DELAY_TICKS;
            if (vault.pendingItems.isEmpty()) {
                vault.closeDelay = CLOSE_DELAY_TICKS;
            }
        } else if (vault.ejectDelay > 0) {
            vault.ejectDelay--;
        }

        // 关闭延迟
        if (vault.closeDelay > 0) {
            vault.closeDelay--;
            if (vault.closeDelay == 0) {
                vault.setInactiveState();
                level.playSound(null, pos, SoundEvents.VAULT_CLOSE_SHUTTER, SoundSource.BLOCKS, 1.0F, 1.0F);
                level.playSound(null, pos, SoundEvents.VAULT_DEACTIVATE, SoundSource.BLOCKS, 1.0F, 1.0F);
            }
        }

        // ===== 持续粒子效果（每 tick 更新冷却，定期生成）=====
        // 获取最新状态，避免参数过时
        BlockState currentState = vault.getBlockState();
        boolean shouldHaveParticles = !vault.opened &&
                (currentState.getValue(TrialVaultBlock.VAULT_STATE) == TrialVaultBlock.VaultState.ACTIVE ||
                        currentState.getValue(TrialVaultBlock.VAULT_STATE) == TrialVaultBlock.VaultState.INACTIVE);

        if (shouldHaveParticles) {
            if (vault.particleCooldown <= 0) {
                vault.addActiveParticles();
                vault.particleCooldown = 5;
            } else {
                vault.particleCooldown--;
            }
        }

        // ===== 玩家靠近检测（带冷却）=====
        if (vault.checkCooldown-- > 0) return;
        vault.checkCooldown = 10;

        boolean playerNear = level.getEntitiesOfClass(Player.class, new AABB(pos).inflate(4.0))
                .stream()
                .anyMatch(p -> p.distanceToSqr(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5) <= 16.0);

        if (playerNear && !vault.opened) {
            if (currentState.getValue(TrialVaultBlock.VAULT_STATE) == TrialVaultBlock.VaultState.INACTIVE) {
                vault.setActiveState();
                level.playSound(null, pos, SoundEvents.VAULT_ACTIVATE, SoundSource.BLOCKS, 1.0F, 1.0F);
                vault.spawnActivateParticles();
            }
        } else if (!playerNear && !vault.opened) {
            boolean far = level.getEntitiesOfClass(Player.class, new AABB(pos).inflate(4.5))
                    .stream()
                    .noneMatch(p -> p.distanceToSqr(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5) <= 20.25);
            if (far && currentState.getValue(TrialVaultBlock.VAULT_STATE) == TrialVaultBlock.VaultState.ACTIVE) {
                vault.setInactiveState();
                level.playSound(null, pos, SoundEvents.VAULT_DEACTIVATE, SoundSource.BLOCKS, 1.0F, 1.0F);
            }
        }
    }

    private void syncToClient() {
        if (level != null && !level.isClientSide) {
            level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), Block.UPDATE_ALL);
        }
    }

    public void clientTick() {
        if (glowTicks > 0) {
            glowTicks--;
            if (glowTicks == 0 && level != null) {
                level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), Block.UPDATE_ALL);
            }
        }
    }

    public InteractionResult use(Player player) {
        if (level == null || level.isClientSide) return InteractionResult.SUCCESS;
        // 冷却检查
        if (cooldownTicks > 0) {
            long remainingSeconds = cooldownTicks / 20;
            long minutes = remainingSeconds / 60;
            long seconds = remainingSeconds % 60;
            player.displayClientMessage(
                    Component.translatable("vault.cooldown", minutes, seconds),
                    true
            );
            level.playSound(null, worldPosition, SoundEvents.VAULT_REJECT_REWARDED_PLAYER, SoundSource.BLOCKS, 1.0F, 1.0F);
            return InteractionResult.CONSUME;
        }
        if (opened) {
            level.playSound(null, worldPosition, SoundEvents.VAULT_REJECT_REWARDED_PLAYER, SoundSource.BLOCKS, 1.0F, 1.0F);
            return InteractionResult.CONSUME;
        }

        boolean canOpen = false;
        ItemStack heldItem = player.getMainHandItem();

        if (type == TrialVaultType.ROOM) {
            // 房间宝库需要 Crimson Crystal
            if (heldItem.getItem() == ModItems.CRIMSON_CRYSTAL.get()) {
                canOpen = true;
                if (!player.getAbilities().instabuild) {
                    heldItem.shrink(1);
                }
            } else {
                // 提示需要 Crimson Crystal
                player.displayClientMessage(Component.translatable("vault.need_crimson_crystal"), true);
            }
        } else {
            // Boss 宝库需要 Stately Key
            if (heldItem.getItem() == ModItems.STATELY_KEY.get()) {
                canOpen = true;
                if (!player.getAbilities().instabuild) {
                    heldItem.shrink(1);
                }
            } else {
                player.displayClientMessage(Component.translatable("vault.need_stately_key"), true);
            }
        }

        if (canOpen) {
            openVault(player);
            return InteractionResult.SUCCESS;
        }

        level.playSound(null, worldPosition, SoundEvents.VAULT_INSERT_ITEM_FAIL, SoundSource.BLOCKS, 1.0F, 1.0F);
        return InteractionResult.CONSUME;
    }

    private void openVault(Player player) {
        opened = true;
        cooldownTicks = COOLDOWN_TICKS;   // 设置冷却
        setChanged();
        syncToClient();

        if (level != null) {
            level.playSound(null, worldPosition, SoundEvents.VAULT_INSERT_ITEM, SoundSource.BLOCKS, 1.0F, 1.0F);
        }
        setInactiveState();   // 正面关闭纹理

        activeToEjectDelay = ACTIVE_TO_EJECT_DELAY_TICKS;
    }


    private List<ItemStack> loadRewardsFromYaml(RegistryAccess registryAccess) {
        String yamlPath = type == TrialVaultType.ROOM ?
                "data/remains_cave/loot_table/vault_rewards/room_reward.yml" :
                "data/remains_cave/loot_table/vault_rewards/boss_reward.yml";

        try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(yamlPath)) {
            if (inputStream == null) {
                return Collections.emptyList();
            }

            Yaml yaml = new Yaml();
            Map<String, Object> data = yaml.load(inputStream);
            List<Map<String, Object>> pools = (List<Map<String, Object>>) data.get("pools");
            List<ItemStack> rewards = new ArrayList<>();
            RandomSource random = level != null ? level.getRandom() : RandomSource.create();

            for (Map<String, Object> pool : pools) {
                int rolls = (int) pool.get("rolls");
                List<Map<String, Object>> entries = (List<Map<String, Object>>) pool.get("entries");

                for (int r = 0; r < rolls; r++) {
                    // 按权重选择条目
                    int totalWeight = 0;
                    for (Map<String, Object> entry : entries) {
                        totalWeight += (int) entry.get("weight");
                    }
                    int selectedWeight = random.nextInt(totalWeight);
                    int cumulative = 0;
                    Map<String, Object> selected = null;
                    for (Map<String, Object> entry : entries) {
                        cumulative += (int) entry.get("weight");
                        if (selectedWeight < cumulative) {
                            selected = entry;
                            break;
                        }
                    }
                    if (selected == null) continue;

                    String entryType = (String) selected.get("type");
                    if ("minecraft:empty".equals(entryType)) {
                        continue;
                    }

                    // 获取物品名称
                    String itemId = (String) selected.get("name");
                    ItemStack stack = null;

                    // 特殊处理附魔书
                    if ("minecraft:enchanted_book".equals(itemId)) {
                        stack = new ItemStack(Items.ENCHANTED_BOOK);
                    } else {
                        // 普通物品，先创建1个占位
                        var item = registryAccess.registryOrThrow(Registries.ITEM).get(ResourceLocation.parse(itemId));
                        if (item == null) {
                            System.err.println("Item not found: " + itemId);
                            continue;
                        }
                        stack = new ItemStack(item, 1);
                    }

                    // 处理数量（优先级：functions中的set_count > 直接count字段）
                    int finalCount = 1; // 默认
                    // 先检查是否有直接的count字段（简化格式）
                    if (selected.containsKey("count")) {
                        List<Integer> countRange = (List<Integer>) selected.get("count");
                        int min = countRange.get(0);
                        int max = countRange.get(1);
                        finalCount = min + random.nextInt(max - min + 1);
                    }

                    // 处理 functions
                    List<Map<String, Object>> functions = (List<Map<String, Object>>) selected.get("functions");
                    if (functions != null) {
                        for (Map<String, Object> func : functions) {
                            String funcName = (String) func.get("function");
                            if ("minecraft:set_count".equals(funcName)) {
                                Map<String, Object> countObj = (Map<String, Object>) func.get("count");
                                String countType = (String) countObj.get("type");
                                if ("minecraft:uniform".equals(countType)) {
                                    double min = (double) countObj.get("min");
                                    double max = (double) countObj.get("max");
                                    finalCount = (int) (min + random.nextDouble() * (max - min + 1));
                                } else {
                                    // 固定值等其他类型暂不支持
                                    finalCount = 1;
                                }
                            } else if ("minecraft:set_enchantments".equals(funcName)) {
                                Map<String, Object> enchantmentsMap = (Map<String, Object>) func.get("enchantments");
                                boolean add = (boolean) func.getOrDefault("add", false);
                                Map<Holder<Enchantment>, Integer> enchantments = new HashMap<>();
                                var enchantmentRegistry = registryAccess.registryOrThrow(Registries.ENCHANTMENT);
                                for (Map.Entry<String, Object> enchEntry : enchantmentsMap.entrySet()) {
                                    String enchId = enchEntry.getKey();
                                    int level = (int) enchEntry.getValue();
                                    var holderOpt = enchantmentRegistry.getHolder(ResourceLocation.parse(enchId));
                                    if (holderOpt.isPresent()) {
                                        enchantments.put(holderOpt.get(), level);
                                    } else {
                                        System.err.println("Enchantment holder not found: " + enchId);
                                    }
                                }
                                // 如果 add 为 false，则清除现有附魔
                                if (!add) {
                                    // 清空附魔，ItemEnchantments.EMPTY 表示无附魔
                                    EnchantmentHelper.setEnchantments(stack, ItemEnchantments.EMPTY);
                                }
                                // 添加新附魔
                                for (Map.Entry<Holder<Enchantment>, Integer> e : enchantments.entrySet()) {
                                    stack.enchant(e.getKey(), e.getValue());
                                }
                            }
                        }
                    }

                    // 设置最终数量（仅对非附魔书有效，附魔书数量始终为1）
                    if (!(stack.getItem() instanceof EnchantedBookItem)) {
                        stack.setCount(finalCount);
                    }

                    rewards.add(stack);
                }
            }
            return rewards;
        } catch (Exception e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }


    // 修改 startEjecting 方法
    public void startEjecting() {
        if (level == null || !opened) return;
        RegistryAccess registryAccess = level.registryAccess();
        List<ItemStack> items = loadRewardsFromYaml(registryAccess);
        if (items.isEmpty()) {
            level.playSound(null, worldPosition, SoundEvents.VAULT_EJECT_ITEM, SoundSource.BLOCKS, 1.0F, 1.0F);
            closeDelay = CLOSE_DELAY_TICKS;
            return;
        }
        pendingItems.clear();
        pendingItems.addAll(items);
        ejectDelay = 0;
    }

    private void setActiveState() {
        BlockState state = getBlockState();
        if (state.getValue(TrialVaultBlock.VAULT_STATE) != TrialVaultBlock.VaultState.ACTIVE) {
            if (level != null) {
                level.setBlock(worldPosition, state.setValue(TrialVaultBlock.VAULT_STATE, TrialVaultBlock.VaultState.ACTIVE), Block.UPDATE_ALL);
            }
        }
    }

    private void setEjectingState() {
        BlockState state = getBlockState();
        if (state.getValue(TrialVaultBlock.VAULT_STATE) != TrialVaultBlock.VaultState.EJECTING) {
            if (level != null) {
                level.setBlock(worldPosition, state.setValue(TrialVaultBlock.VAULT_STATE, TrialVaultBlock.VaultState.EJECTING), Block.UPDATE_ALL);
            }
        }
    }

    private void setInactiveState() {
        BlockState state = getBlockState();
        if (state.getValue(TrialVaultBlock.VAULT_STATE) != TrialVaultBlock.VaultState.INACTIVE) {
            if (level != null) {
                level.setBlock(worldPosition, state.setValue(TrialVaultBlock.VAULT_STATE, TrialVaultBlock.VaultState.INACTIVE), Block.UPDATE_ALL);
            }
        }
    }

    private void spawnItem(ItemStack stack) {
        if (level == null) return;
        double x = worldPosition.getX() + 0.5;
        double y = worldPosition.getY() + 1.0;
        double z = worldPosition.getZ() + 0.5;
        ItemEntity itemEntity = new ItemEntity(level, x, y, z, stack);
        itemEntity.setDefaultPickUpDelay();
        level.addFreshEntity(itemEntity);
        level.addParticle(ParticleTypes.POOF, x, y, z, 0, 0.1, 0);
    }
}