package com.shanebeestudios.skbee.api.wrapper;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.configuration.serialization.SerializableAs;
import org.bukkit.util.NumberConversions;
import org.jetbrains.annotations.NotNull;

import java.util.Map;

/**
 * Represents a lazy version of {@link Location}
 * <p>These are used in {@link com.shanebeestudios.skbee.api.bound.Bound Bounds}
 * when a world might not have been loaded yet
 */
@SerializableAs("LazyLocation")
public class LazyLocation extends Location {

    private String worldName;
    private NamespacedKey worldKey;

    public LazyLocation(World world, double x, double y, double z, float yaw, float pitch) {
        super(world, x, y, z, yaw, pitch);
        this.worldKey = world.getKey();
    }

    @Deprecated(forRemoval = true, since = "3.24.0")
    public LazyLocation(String worldName, double x, double y, double z, float yaw, float pitch) {
        super(null, x, y, z, yaw, pitch);
        this.worldName = worldName;
    }

    public LazyLocation(NamespacedKey worldKey, double x, double y, double z, float yaw, float pitch) {
        super(null, x, y, z, yaw, pitch);
        this.worldKey = worldKey;
    }

    public LazyLocation(Location location) {
        super(location.getWorld(), location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
    }

    /**
     * Get the {@link Location} this LazyLocation holds.
     * <p>Will attempt to check/update the world
     *
     * @return Location with hopefully updated world
     */
    public Location getLocation() {
        if (super.getWorld() == null) {
            World world = Bukkit.getWorld(this.worldName);
            if (world != null) this.setWorld(world);
        }
        return this;
    }

    /**
     * @hidden Copied from {@link Location Bukkit Location}
     */
    @Override
    public @NotNull Map<String, Object> serialize() {
        Map<String, Object> data = super.serialize();

        if (!data.containsKey("world") && this.worldName != null) {
            data.put("world", this.worldName);
        }
        if (!data.containsKey("world_key") && this.worldKey != null) {
            data.put("world_key", this.worldKey.toString());
        }

        return data;
    }

    /**
     * @hidden Copied from {@link Location Bukkit Location}
     */
    @NotNull
    public static Location deserialize(@NotNull Map<String, Object> args) {
        World world = null;
        if (args.containsKey("world")) {
            String name = (String) args.get("world");
            world = Bukkit.getWorld(name);
            if (world == null) {
                return new LazyLocation(name, NumberConversions.toDouble(args.get("x")), NumberConversions.toDouble(args.get("y")), NumberConversions.toDouble(args.get("z")), NumberConversions.toFloat(args.get("yaw")), NumberConversions.toFloat(args.get("pitch")));
            }
        }
        if (args.containsKey("world_key")) {
            String name = (String) args.get("world_key");
            NamespacedKey key = NamespacedKey.fromString(name);
            assert key != null : "NamespacedKey cannot be null";
            world = Bukkit.getWorld(key);
            if (world == null) {
                return new LazyLocation(key, NumberConversions.toDouble(args.get("x")), NumberConversions.toDouble(args.get("y")), NumberConversions.toDouble(args.get("z")), NumberConversions.toFloat(args.get("yaw")), NumberConversions.toFloat(args.get("pitch")));
            }
        }
        return new LazyLocation(world, NumberConversions.toDouble(args.get("x")), NumberConversions.toDouble(args.get("y")), NumberConversions.toDouble(args.get("z")), NumberConversions.toFloat(args.get("yaw")), NumberConversions.toFloat(args.get("pitch")));
    }

}
