/*
 * 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.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;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 红包冷却数据保存类
 * 用于持久化存储玩家的冷却时间和每日发送次数
 */
public class RedEnvelopeCooldownData extends SavedData {
    private static final String DATA_NAME = "redenvelope_cooldown";

    // 玩家最后一次发送各种类型红包的时间戳
    private final Map<UUID, Map<String, Long>> playerLastSendTime = new ConcurrentHashMap<>();

    // 玩家每日发送次数
    private final Map<UUID, Integer> playerDailyCount = new ConcurrentHashMap<>();

    // 记录上次重置的日期
    private int lastResetDay = -1;

    // ===== SavedData 必须的方法 =====

    @Override
    public @NotNull CompoundTag save(@NotNull CompoundTag tag) {
        // 保存玩家冷却时间数据
        ListTag playersList = new ListTag();
        for (Map.Entry<UUID, Map<String, Long>> entry : playerLastSendTime.entrySet()) {
            CompoundTag playerTag = new CompoundTag();

            // 保存玩家UUID
            playerTag.putUUID("uuid", entry.getKey());

            // 保存该玩家各类型红包的冷却时间
            CompoundTag cooldownsTag = new CompoundTag();
            for (Map.Entry<String, Long> cooldownEntry : entry.getValue().entrySet()) {
                cooldownsTag.putLong(cooldownEntry.getKey(), cooldownEntry.getValue());
            }
            playerTag.put("cooldowns", cooldownsTag);

            // 保存该玩家每日发送次数
            Integer dailyCount = playerDailyCount.get(entry.getKey());
            if (dailyCount != null) {
                playerTag.putInt("dailyCount", dailyCount);
            }

            playersList.add(playerTag);
        }
        tag.put("players", playersList);

        // 保存上次重置日期
        tag.putInt("lastResetDay", lastResetDay);

        return tag;
    }

    /**
     * 从NBT加载数据
     */
    public static @NotNull RedEnvelopeCooldownData load(@NotNull CompoundTag tag) {
        RedEnvelopeCooldownData data = new RedEnvelopeCooldownData();

        // 加载玩家数据
        ListTag playersList = tag.getList("players", Tag.TAG_COMPOUND);
        for (int i = 0; i < playersList.size(); i++) {
            CompoundTag playerTag = playersList.getCompound(i);

            UUID uuid = playerTag.getUUID("uuid");

            // 加载冷却时间
            CompoundTag cooldownsTag = playerTag.getCompound("cooldowns");
            Map<String, Long> cooldowns = new HashMap<>();
            for (String key : cooldownsTag.getAllKeys()) {
                cooldowns.put(key, cooldownsTag.getLong(key));
            }
            data.playerLastSendTime.put(uuid, cooldowns);

            // 加载每日次数
            if (playerTag.contains("dailyCount")) {
                data.playerDailyCount.put(uuid, playerTag.getInt("dailyCount"));
            }
        }

        // 加载上次重置日期
        if (tag.contains("lastResetDay")) {
            data.lastResetDay = tag.getInt("lastResetDay");
        }

        return data;
    }

    /**
     * 获取或创建数据实例（工厂方法）
     */
    public static @NotNull RedEnvelopeCooldownData getOrCreate(@NotNull ServerLevel level) {
        return level.getDataStorage().computeIfAbsent(
                RedEnvelopeCooldownData::load,
                RedEnvelopeCooldownData::new,
                DATA_NAME
        );
    }

    // ===== 业务方法 =====

    /**
     * 检查并重置每日数据
     */
    private void checkAndResetDaily() {
        int today = java.time.LocalDate.now().getDayOfYear();
        if (today != lastResetDay) {
            playerDailyCount.clear();
            lastResetDay = today;
            this.setDirty(); // 标记数据已修改，需要保存
        }
    }

    /**
     * 记录玩家发送红包
     */
    public void recordSend(UUID playerUuid, @MagicConstant(stringValues = {REUtil.COMMON, REUtil.SPELL, REUtil.LUCK}) String type) {
        long now = System.currentTimeMillis() / 1000;

        // 更新冷却时间
        Map<String, Long> stringLongMap = playerLastSendTime.computeIfAbsent(playerUuid, k -> new ConcurrentHashMap<>());
        stringLongMap.put(type, now);
        stringLongMap.put("default", now);

        // 更新每日计数
        checkAndResetDaily();
        playerDailyCount.merge(playerUuid, 1, Integer::sum);

        this.setDirty(); // 标记需要保存
    }

    /**
     * 获取玩家指定类型红包的剩余冷却时间
     */
    public long getRemainingCooldown(UUID playerUuid, @Nullable @MagicConstant(stringValues = {REUtil.COMMON, REUtil.SPELL, REUtil.LUCK}) String type, int cooldownSeconds) {
        if (cooldownSeconds <= 0) return 0;

        Map<String, Long> playerCooldowns = playerLastSendTime.get(playerUuid);
        if (playerCooldowns == null) return 0;
        Long lastTime;
        if (type != null)
            lastTime = playerCooldowns.getOrDefault(type, playerCooldowns.get("default"));
        else lastTime = playerCooldowns.get("default");
        if (lastTime == null) return 0;

        long now = System.currentTimeMillis() / 1000;
        long elapsed = now - lastTime;

        return elapsed < cooldownSeconds ? cooldownSeconds - elapsed : 0;
    }

    /**
     * 获取玩家今日已发送数量
     */
    public int getTodaySendCount(UUID playerUuid) {
        checkAndResetDaily();
        return playerDailyCount.getOrDefault(playerUuid, 0);
    }

    /**
     * 获取玩家今日剩余可发送数量
     */
    public int getRemainingDailyLimit(UUID playerUuid, int dailyLimit) {
        if (dailyLimit <= 0) return Integer.MAX_VALUE;
        return dailyLimit - getTodaySendCount(playerUuid);
    }

    /**
     * 检查玩家是否可以发送红包
     */
    public boolean canSend(UUID playerUuid, String type, int cooldownSeconds, int dailyLimit) {
        // 检查每日上限
        if (dailyLimit > 0 && getTodaySendCount(playerUuid) >= dailyLimit) {
            return false;
        }

        // 检查冷却时间
        return cooldownSeconds <= 0 || getRemainingCooldown(playerUuid, type, cooldownSeconds) <= 0;
    }

}