package net.miarma.hystuff.common;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.core.universe.world.World;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class HomeManager {
    private static final String HOMES_FILE = "homes.json";
    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
    private static final Type HOMES_TYPE = new TypeToken<Map<String, Location>>(){}.getType();
    private final Path dataDir;
    private final HytaleLogger LOGGER;
    private final Map<UUID, Location> homes = new ConcurrentHashMap<>();

    public HomeManager(@Nonnull Path dataDir, @Nonnull HytaleLogger logger) {
        this.dataDir = dataDir;
        this.LOGGER = logger;
        this.load();
    }

    private void load() {
        Path homesFile = this.dataDir.resolve(HOMES_FILE);
        if(!Files.exists(homesFile)) {
            this.LOGGER.atInfo().log("No homes file found. Creating.");
        } else {
            try {
                String jsonStr = Files.readString(homesFile);
                Map<String, Location> loaded = GSON.fromJson(jsonStr, HOMES_TYPE);
                if (loaded != null) {
                    for(Map.Entry<String, Location> entry : loaded.entrySet()) {
                        try {
                            UUID uuid = UUID.fromString(entry.getKey());
                            this.homes.put(uuid, entry.getValue());
                        } catch (IllegalArgumentException e) {
                            this.LOGGER.atWarning().log("Invalid player UUID in homes file: %s", entry.getKey());
                        }
                    }
                    this.LOGGER.atInfo().log("Loaded %d homes from disk.", this.homes.size());
                }
            } catch (IOException e) {
                this.LOGGER.atSevere().log("Failed to load homes from disk: %s", e.getMessage());
            }
        }
    }

    public void save() {
        try {
            if(!Files.exists(this.dataDir)) {
                Files.createDirectories(this.dataDir);
            }

            Map<String, Location> toSave = new ConcurrentHashMap<>();

            for(Map.Entry<UUID, Location> entry : this.homes.entrySet()) {
              toSave.put(entry.getKey().toString(), entry.getValue());
            }

            Path homesFile = this.dataDir.resolve(HOMES_FILE);
            String jsonStr = GSON.toJson(toSave, HOMES_TYPE);
            Files.writeString(homesFile, jsonStr);
            this.LOGGER.atFine().log("Saved %d homes to disk.", this.homes.size());
        } catch (IOException e) {
            this.LOGGER.atSevere().log("Failed to save homes to disk: %s", e.getMessage());
        }
    }

    public void setHome(@Nonnull UUID playerUuid, @Nonnull World world, @Nonnull Vector3d position, @Nonnull Vector3f rotation) {
        Location home = new Location(world.getName(), position.getX(), position.getY(), position.getZ(), rotation.getYaw(), rotation.getPitch());
        this.homes.put(playerUuid, home);
        this.save();
    }

    @Nullable
    public Location getHome(@Nonnull UUID playerUuid) {
        return this.homes.get(playerUuid);
    }

    public boolean hasHome(@Nonnull UUID playerUuid) {
        return this.homes.containsKey(playerUuid);
    }

    public void removeHome(@Nonnull UUID playerUuid) {
        this.homes.remove(playerUuid);
        this.save();
    }

    public int getHomeCount() {
        return this.homes.size();
    }
}
