/*
 *  Copyright 2025-2026 R3944Realms
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package top.r3944realms.eroticdungeongame.core.storage;

import com.mojang.datafixers.DataFixer;
import com.mojang.logging.LogUtils;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.slf4j.Logger;
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCServerPlayer;

import javax.annotation.Nullable;
import java.io.File;
import java.util.*;
import java.util.function.Function;

public class NPCDataStorage {
    private static final Logger LOGGER = LogUtils.getLogger();
    private final File npcDir;
    protected final DataFixer fixerUpper;
    public static LevelResource NPC_DATA_DIR =  new LevelResource("npcdata");
    public NPCDataStorage(LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer fixerUpper) {
        this.fixerUpper = fixerUpper;
        this.npcDir = levelStorageAccess.getLevelPath(NPC_DATA_DIR).toFile();
        this.npcDir.mkdirs();
    }


    /**
     * 删除指定 NPC 的数据文件
     * @param npcUUID 要删除的 NPC 的 UUID
     * @return 是否删除成功
     */
    public boolean deleteNPCData(UUID npcUUID) {
        if (npcUUID == null) {
            LOGGER.warn("Cannot delete NPC data: UUID is null");
            return false;
        }

        try {
            File dataFile = new File(this.npcDir, npcUUID + ".dat");
            File oldFile = new File(this.npcDir, npcUUID + ".dat_old");

            boolean deleted = false;

            // 删除主数据文件
            if (dataFile.exists()) {
                deleted = dataFile.delete();
                if (deleted) {
                    LOGGER.info("Deleted NPC data file: {}", dataFile.getName());
                } else {
                    LOGGER.warn("Failed to delete NPC data file: {}", dataFile.getName());
                }
            }

            // 删除备份文件
            if (oldFile.exists()) {
                boolean oldDeleted = oldFile.delete();
                if (oldDeleted) {
                    LOGGER.debug("Deleted NPC backup file: {}", oldFile.getName());
                }
            }

            return deleted;

        } catch (Exception e) {
            LOGGER.error("Failed to delete NPC data for UUID: {}", npcUUID, e);
            return false;
        }
    }

    /**
     * 删除指定 NPC 的数据文件（通过 NPCServerPlayer 对象）
     * @param npc 要删除的 NPC
     * @return 是否删除成功
     */
    public boolean deleteNPCData(NPCServerPlayer npc) {
        if (npc == null) {
            LOGGER.warn("Cannot delete NPC data: NPC is null");
            return false;
        }

        return deleteNPCData(npc.getUUID());
    }

    public void save(NPCServerPlayer player) {
        try {
            CompoundTag compoundtag = player.saveWithoutId(new CompoundTag());
            File file1 = File.createTempFile(player.getStringUUID() + "-", ".dat", this.npcDir);
            NbtIo.writeCompressed(compoundtag, file1);
            File file2 = new File(this.npcDir, player.getStringUUID() + ".dat");
            File file3 = new File(this.npcDir, player.getStringUUID() + ".dat_old");
            Util.safeReplaceFile(file2, file1, file3);
            net.minecraftforge.event.ForgeEventFactory.firePlayerSavingEvent(player, npcDir, player.getStringUUID());
        } catch (Exception exception) {
            LOGGER.warn("Failed to save player data for {}", player.getName().getString());
        }

    }
    @Nullable
    public NPCServerPlayer load(UUID uuid, Function<CompoundTag, NPCServerPlayer> loader) {
        CompoundTag compoundtag = null;

        try {
            File file1 = new File(this.npcDir, uuid + ".dat");
            if (file1.exists() && file1.isFile()) {
                compoundtag = NbtIo.readCompressed(file1);
            }
        } catch (Exception exception) {
            LOGGER.warn("Failed to checkInit player data for {}", uuid, exception);
        }
        NPCServerPlayer npcServerPlayer = loader.apply(compoundtag);
        if (compoundtag != null) {
            int i = NbtUtils.getDataVersion(compoundtag, -1);
            npcServerPlayer.load(DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, compoundtag, i));
        }

        net.minecraftforge.event.ForgeEventFactory.firePlayerLoadingEvent(npcServerPlayer, npcDir, uuid.toString());
        return npcServerPlayer;
    }
    @Nullable
    public CompoundTag load(NPCServerPlayer npcServerPlayer) {
        CompoundTag compoundtag = null;

        try {
            File file1 = new File(this.npcDir, npcServerPlayer.getStringUUID() + ".dat");
            if (file1.exists() && file1.isFile()) {
                compoundtag = NbtIo.readCompressed(file1);
            }
        } catch (Exception exception) {
            LOGGER.warn("Failed to checkInit player data for {}", npcServerPlayer.getName().getString());
        }

        if (compoundtag != null) {
            int i = NbtUtils.getDataVersion(compoundtag, -1);
            npcServerPlayer.load(DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, compoundtag, i));
        }
        net.minecraftforge.event.ForgeEventFactory.firePlayerLoadingEvent(npcServerPlayer, npcDir, npcServerPlayer.getStringUUID());

        return compoundtag;
    }

    public String[] getSeenNPCs() {
        String[] astring = this.npcDir.list();
        Set<String> ret = new HashSet<>();
        if (astring == null) {
            astring = new String[0];
        } else {
            for (String s : astring) {
                if (s.endsWith(".dat") && !s.endsWith(".dat_old")) {
                    ret.add(s.substring(0, s.length() - 4));
                }
            }
            astring = ret.toArray(new String[0]);
        }
        return astring;
    }

    public File getNPCDataFolder() {
        return npcDir;
    }
}
