/*
 * Original project: RedEnvelope by KaguyaStudio-MC
 * Original project link: https://github.com/KaguyaStudio-MC/RedEnvelope
 * Modified by: LeisureTimeDock
 * This file is part of RedEnvelope
 * Licensed under Camellia License Version 1.0
 */

package top.leisuretimedock.redenvelope.core.util;

import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.ItemStack;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.leisuretimedock.redenvelope.RedEnvelope;
import top.leisuretimedock.redenvelope.core.capability.RECapability;
import top.leisuretimedock.redenvelope.core.config.ServerConfig;
import top.leisuretimedock.redenvelope.core.network.PlaySoundToClientPacket;
import top.leisuretimedock.redenvelope.core.network.RENetworkHandler;
import top.leisuretimedock.redenvelope.core.network.RedEnvelopePacket;
import top.leisuretimedock.redenvelope.core.capability.RedEnvelopeStorage;

import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

public class REUtil {
    public static final String COMMON = "common";
    public static final String SPELL = "spell";
    public static final String LUCK = "luck";
    public static @NotNull Optional<RedEnvelopePacket> create(@NotNull ServerPlayer player, ItemStack itemStack, @MagicConstant(stringValues = {COMMON, SPELL, LUCK}) String hbtype, int count) {
        RedEnvelopeCooldownData data = RedEnvelopeCooldownData.getOrCreate(player.serverLevel());
        boolean canSend = data.canSend(player.getUUID(), hbtype, ServerConfig.getCooldownForType(hbtype), ServerConfig.DAILY_LIMIT.get());
        int remainingDailyLimit = data.getRemainingDailyLimit(player.getUUID(), ServerConfig.DAILY_LIMIT.get());
        if (canSend) {
            data.recordSend(player.getUUID(), hbtype);
            CompoundTag compoundTag = new CompoundTag();
            compoundTag.putUUID("from", player.getUUID());
            compoundTag.putBoolean("opened", false);
            compoundTag.putLong("time", (System.currentTimeMillis() / 1000));
            if (ServerConfig.DAILY_LIMIT.get() > 0)
                player.displayClientMessage(Component.translatable("chat.red_envelope.daily_remaining", remainingDailyLimit), false);
            return Optional.of(new RedEnvelopePacket(itemStack, hbtype, count, UUID.randomUUID(), compoundTag));
        }
        if (remainingDailyLimit <= 0) {
            player.displayClientMessage(Component.translatable("chat.red_envelope.daily_limit_reached"), false);
            playSoundForPlayer(player, SoundEvents.ITEM_BREAK);
        } else {
            long remainingCooldown = data.getRemainingCooldown(player.getUUID(), hbtype, ServerConfig.getCooldownForType(hbtype));
            if (remainingCooldown > 0) {
                player.displayClientMessage(Component.translatable("chat.red_envelope.cooldown", remainingCooldown), true);
                playSoundForPlayer(player, SoundEvents.ITEM_BREAK);
            }
        }
        return Optional.empty();
    }

    public static void receiveMessage(@NotNull ServerPlayer player, @NotNull RedEnvelopePacket packet) {
        CompoundTag compoundTag = packet.extraData();
        UUID from = compoundTag.getUUID("from");

        // 安全检查：确保玩家在线
        ServerPlayer fromPlayer = Objects.requireNonNull(player.getServer()).getPlayerList().getPlayer(from);
        if (fromPlayer == null) return;

        Component fromDisplayName = fromPlayer.getDisplayName();
        MutableComponent component = Component.translatable("chat.red_envelope.receive." + packet.hbtype(), fromDisplayName);

        if (!packet.hbtype().equals("spell")) {
            component.append(Component.translatable("chat.red_envelope.open")
                    .withStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/hongbao open " + packet.uuid())).withUnderlined(true))
                    .withStyle(ChatFormatting.GREEN));
        } else {
            component.append(Component.literal("[").append(packet.extraData().getString("spell")).append("]").withStyle(ChatFormatting.LIGHT_PURPLE))
                    .append(Component.translatable("chat.red_envelope.send")
                            .withStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, packet.extraData().getString("spell"))).withUnderlined(true))
                            .withStyle(ChatFormatting.GREEN));
        }
        playSoundForPlayer(player, SoundEvents.PLAYER_LEVELUP);
        player.sendSystemMessage(component);
        RedEnvelope.LOGGER.debug("receive red envelope");
    }

    public static void open(@NotNull ServerPlayer player, UUID uuid) {
        // 获取玩家的红包数据
        player.getCapability(RECapability.HONGBAO_STORAGE).ifPresent(storage -> {
            RedEnvelopePacket redEnvelopePacket = storage.get(uuid);
            if (redEnvelopePacket == null) {
                return;
            }
            if (redEnvelopePacket.extraData().getBoolean("opened")) {
                if (!ServerConfig.ALLOW_MULTIPLE_CLAIM.get()) {
                    player.sendSystemMessage(Component.translatable("chat.red_envelope.opened.done"), true);
                    playSoundForPlayer(player, SoundEvents.VILLAGER_NO);
                    return;
                }
            }

            CompoundTag compoundTag = redEnvelopePacket.extraData();
            UUID from = compoundTag.getUUID("from");
            ServerPlayer fromPlayer = Objects.requireNonNull(player.getServer()).getPlayerList().getPlayer(from);
            if (fromPlayer == null) return;
            if (!ServerConfig.ALLOW_CLAIM_OWN.get() && Objects.equals(from, player.getUUID())) {
                player.displayClientMessage(Component.translatable("chat.red_envelope.cannot_claim_own"), true);
                playSoundForPlayer(player, SoundEvents.VILLAGER_NO);
                return;
            }
            // 获取发送者的红包数据
            fromPlayer.getCapability(RECapability.HONGBAO_STORAGE).ifPresent(fromStorage -> {
                RedEnvelopePacket real = fromStorage.get(uuid);
                if (real == null) return;

                RedEnvelopePacket left = openInternal(player, real);

                if (left != null) {
                    if (left.count() >= 1) {
                        fromStorage.replace(uuid, left);
                    } else {
                        fromStorage.remove(uuid);
                    }
                } else {
                    destroy(player.getServer(), uuid);
                }

                if (left != null) {
                    if (fromPlayer.getUUID() != player.getUUID()) {
                        // 更新当前玩家的数据
                        storage.replace(uuid, left);
                        left.extraData().putBoolean("opened", true);
                    } else {
                        left.extraData().putBoolean("opened", true);
                        fromStorage.replace(uuid, left);
                    }
                }
                playSoundForPlayer(player, SoundEvents.PLAYER_LEVELUP);
                // 同步数据到客户端
                RENetworkHandler.sendToPlayer(storage.packet(), player);
                RENetworkHandler.sendToPlayer(fromStorage.packet(), fromPlayer);
            });
        });
    }

    public static void query(@NotNull ServerPlayer player) {
        player.getCapability(RECapability.HONGBAO_STORAGE).ifPresent(storage -> {
            RedEnvelopeStorage copy = new RedEnvelopeStorage(storage);
            copy.keys().forEach(uuid -> query(player, uuid));
        });
    }

    public static void queryList(@NotNull ServerPlayer player) {
        player.getCapability(RECapability.HONGBAO_STORAGE).ifPresent(storage -> {
            RedEnvelopeStorage copy = new RedEnvelopeStorage(storage);
            copy.keys().forEach(uuid -> {
                player.sendSystemMessage(Component.literal(uuid.toString()).append(
                        Component.translatable("chat.red_envelope.action.query")
                                .withStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/hongbao query " + uuid)).withUnderlined(true))
                ));
            });
        });
    }

    public static void query(@NotNull ServerPlayer player, UUID uuid) {
        RedEnvelope.LOGGER.debug("query red_envelope {}", uuid);

        player.getCapability(RECapability.HONGBAO_STORAGE).ifPresent(storage -> {
            RedEnvelopePacket redEnvelopePacket = storage.get(uuid);
            if (redEnvelopePacket == null) {
                RedEnvelope.LOGGER.debug("red_envelope not found");
                return;
            }

            CompoundTag compoundTag = redEnvelopePacket.extraData();
            UUID from = compoundTag.getUUID("from");
            boolean opened = compoundTag.getBoolean("opened");

            ServerPlayer fromPlayer = Objects.requireNonNull(player.getServer()).getPlayerList().getPlayer(from);
            if (fromPlayer == null) return;

            fromPlayer.getCapability(RECapability.HONGBAO_STORAGE).ifPresent(fromStorage -> {
                RedEnvelopePacket real = fromStorage.get(uuid);
                if (real == null) return;

                MutableComponent component = Component.translatable("chat.red_envelope.query." + real.hbtype());

                MutableComponent returned = Component.empty();
                ItemStack itemStack = real.itemStack();
                returned.append(String.valueOf(itemStack.getCount())).append("x ").append(itemStack.getDisplayName());

                component.append(Component.translatable("chat.red_envelope.query.from", fromPlayer.getDisplayName(), real.count(), returned).append(" "));

                if (opened) {
                    component.append(Component.translatable("chat.red_envelope.query.opened")
                            .withStyle(ChatFormatting.AQUA));
                } else {
                    component.append(Component.translatable("chat.red_envelope.query.unopened")
                            .withStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/hongbao open " + real.uuid())).withUnderlined(true))
                            .withStyle(ChatFormatting.GREEN));
                }

                if (player.getUUID() == fromPlayer.getUUID()) {
                    component.append(Component.translatable("chat.red_envelope.action.withdraw")
                            .withStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/hongbao withdraw " + uuid)).withUnderlined(true)));
                }

                player.sendSystemMessage(component);
            });
        });
    }

    public static void destroy(@NotNull ServerPlayer player, UUID uuid) {
        player.getCapability(RECapability.HONGBAO_STORAGE).ifPresent(storage -> {
            storage.remove(uuid);
            // 同步到客户端
            RENetworkHandler.sendToPlayer(storage.packet(), player);
        });
    }

    public static void destroy(MinecraftServer server, UUID uuid) {
        if (server == null) return;
        server.getPlayerList().getPlayers().forEach(player -> destroy(player, uuid));
    }
    public final static int TIME_OUT = 1;
    public final static int WITHDRAW = 2;
    public final static int PLAYER_EXIT = 3;

    public static void withdraw(@NotNull MinecraftServer server, @NotNull RedEnvelopePacket packet, @MagicConstant(intValues = {TIME_OUT, WITHDRAW, PLAYER_EXIT}) int type) {
        CompoundTag compoundTag = packet.extraData();
        UUID from = compoundTag.getUUID("from");
        ServerPlayer fromPlayer = server.getPlayerList().getPlayer(from);
        if (fromPlayer == null) return;

        fromPlayer.getCapability(RECapability.HONGBAO_STORAGE).ifPresent(storage -> {
            RedEnvelopePacket real = storage.get(packet.uuid());
            if (real == null) return;

            MutableComponent returned = Component.empty();
            ItemStack itemStack = real.itemStack();

            if (Objects.equals(real.hbtype(), "common")) {
                itemStack.setCount(real.count() * real.extraData().getInt("single"));
            }

            fromPlayer.addItem(itemStack.copy());
            returned.append(String.valueOf(itemStack.getCount())).append("x ").append(itemStack.getDisplayName());

            switch (type) {
                case TIME_OUT:
                    fromPlayer.sendSystemMessage(Component.translatable("chat.red_envelope.returned", returned));
                    REUtil.playSoundForPlayer(fromPlayer, SoundEvents.CHEST_CLOSE);
                    break;
                case WITHDRAW:
                    fromPlayer.sendSystemMessage(Component.translatable("chat.red_envelope.withdraw", returned));
                    REUtil.playSoundForPlayer(fromPlayer, SoundEvents.CHEST_CLOSE);
                    break;
                case PLAYER_EXIT:
                    break;
            }

            destroy(fromPlayer, real.uuid());
        });
    }

    @Nullable
    private static RedEnvelopePacket openInternal(ServerPlayer player, @NotNull RedEnvelopePacket packet) {
        String hbtype = packet.hbtype();
        int remainingClaims = packet.count();      // 剩余可领取次数
        ItemStack remainingStack = packet.itemStack(); // 剩余物品
        int remainingItems = remainingStack.getCount();
        if (!remainingStack.isEmpty()) {
            MutableComponent got = Component.empty();
            RedEnvelopePacket left = packet;

            if (hbtype.equals("common")) {
                // 普通红包：固定数量
                int single = packet.extraData().getInt("single");
                int giveAmount = Math.min(single, remainingItems);

                giveItems(player, remainingStack, giveAmount, got);
                left = createLeftover(packet, remainingStack, remainingClaims - 1);

            } else if (hbtype.equals("luck") || hbtype.equals("spell")) {
                // 运气红包：随机数量，可能空包
                if (remainingClaims == 1) {
                    // 最后一次，全给
                    giveItems(player, remainingStack, remainingItems, got);
                    left = null;
                } else {
                    // 还有多次机会
                    int giveAmount = calculateGiveAmount(remainingItems, remainingClaims, player.getRandom());
                    giveItems(player, remainingStack, giveAmount, got);
                    left = createLeftover(packet, remainingStack, remainingClaims - 1);
                }
            }

            player.sendSystemMessage(Component.translatable("chat.red_envelope.opened." + hbtype, got));
            return left;
        } else {
            return null;
        }
    }

    private static int calculateGiveAmount(int remainingItems, int remainingClaims, RandomSource random) {
        if (remainingItems <= 0) return 0;
        if (remainingClaims <= 1) return remainingItems;

        // 20%概率空包，但确保不会导致最后不够分
        if (random.nextFloat() < 0.2f && remainingItems < remainingClaims) {
            return 0;
        }

        // 随机分配，但保留至少 (remainingClaims-1) 个物品给后面
        int maxGive = Math.min(remainingItems, remainingItems - (remainingClaims - 1));
        return maxGive > 0 ? random.nextIntBetweenInclusive(1, maxGive) : 1;
    }

    private static void giveItems(ServerPlayer player, ItemStack stack, int amount, MutableComponent got) {
        if (amount <= 0 || stack.isEmpty()) {
            got.append(Component.translatable("chat.red_envelope.empty"));
            return;
        }

        ItemStack copy = stack.copy();
        copy.setCount(amount);
        player.addItem(copy);
        got.append(String.valueOf(amount)).append("x ").append(stack.getDisplayName());
        stack.shrink(amount);
    }

    @NotNull
    private static RedEnvelopePacket createLeftover(RedEnvelopePacket original, ItemStack remainingStack, int newCount) {
        return new RedEnvelopePacket(
                remainingStack.isEmpty() ? ItemStack.EMPTY : remainingStack,
                original.hbtype(),
                newCount,
                original.uuid(),
                original.extraData()
        );
    }

    public static void queryRemain(ServerPlayer player) {
        RedEnvelopeCooldownData data = RedEnvelopeCooldownData.getOrCreate(player.serverLevel());
        int remainingDailyLimit = data.getRemainingDailyLimit(player.getUUID(), ServerConfig.DAILY_LIMIT.get());
        player.displayClientMessage(Component.translatable("chat.red_envelope.daily_remaining", remainingDailyLimit), false);
    }

    public static void queryCooldown(ServerPlayer player, @Nullable @MagicConstant(stringValues = {REUtil.COMMON, REUtil.SPELL, REUtil.LUCK}) String type) {
        RedEnvelopeCooldownData data = RedEnvelopeCooldownData.getOrCreate(player.serverLevel());
        long remainingDailyLimit = data.getRemainingCooldown(player.getUUID(), type, ServerConfig.getCooldownForType(type));
        player.displayClientMessage(Component.translatable("chat.red_envelope.cooldown", remainingDailyLimit), false);
    }

    public static void playSoundForPlayer(ServerPlayer targetPlayer, SoundEvent sound) {
        RENetworkHandler.sendToPlayer(
                new PlaySoundToClientPacket(
                    sound,
                    SoundSource.PLAYERS,
                    1.0f,
                    1.0f,
                    targetPlayer.getX(),
                    targetPlayer.getY(),
                    targetPlayer.getZ()
                ),
                targetPlayer
        );
    }


}