package org.allaymc.api.world.storage;

import org.allaymc.api.entity.Entity;
import org.allaymc.api.registry.Registries;
import org.allaymc.api.world.World;
import org.allaymc.api.world.WorldData;
import org.allaymc.api.world.chunk.Chunk;
import org.allaymc.api.world.dimension.DimensionType;
import org.jetbrains.annotations.ApiStatus;

import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * WorldStorage represents the storage of a world, which is responsible for reading and writing chunks and world data.
 * The instance of WorldStorage will be created for each world and won't be shared between worlds.
 * <p>
 * The implementation of these methods should all be thread-safe.
 *
 * @author daoge_cmd | Cool_Loong
 */
public interface WorldStorage {
    /**
     * This method will be called every world tick.
     *
     * @param currentTick the current world tick
     */
    @ApiStatus.OverrideOnly
    default void tick(long currentTick) {
    }

    /**
     * This method will be called before the first tick of the world. The implementation can decide
     * whether to hold the world object (if the implementation needs the world object during runtime).
     * Note that user is not allowed to call this method.
     *
     * @param world the world object
     */
    @ApiStatus.OverrideOnly
    default void setWorld(World world) {
    }

    /**
     * Start batch write mode. In this mode, chunk writes via {@link #writeChunk(Chunk)} will be
     * accumulated and flushed in a single I/O operation when {@link #flushBatchWrite()} is called.
     * This significantly reduces write overhead when saving many chunks at once (e.g., on shutdown).
     */
    @ApiStatus.OverrideOnly
    default void startBatchWrite() {
    }

    /**
     * Flush all accumulated chunk writes in a single batch operation and exit batch write mode.
     * Must be called after {@link #startBatchWrite()}.
     */
    @ApiStatus.OverrideOnly
    default void flushBatchWrite() {
    }

    /**
     * Shutdown the storage.
     */
    default void shutdown() {
    }

    /**
     * Read a chunk from the storage.
     *
     * @param chunkX        the x coordinate of the chunk
     * @param chunkZ        the z coordinate of the chunk
     * @param dimensionType the dimension type of the chunk
     * @return a chunk future
     */
    CompletableFuture<Chunk> readChunk(int chunkX, int chunkZ, DimensionType dimensionType);

    /**
     * Similar to {@link #readChunk(int, int, DimensionType)} but is synchronous.
     */
    Chunk readChunkSync(int chunkX, int chunkZ, DimensionType dimensionType);

    /**
     * Write a chunk to the storage.
     *
     * @param chunk the chunk to write
     * @return a future which will be completed when the chunk is written
     */
    CompletableFuture<Void> writeChunk(Chunk chunk);

    /**
     * Similar to {@link #writeChunk(Chunk)} but is synchronous.
     */
    void writeChunkSync(Chunk chunk);

    /**
     * Read the entities in a chunk.
     *
     * @param chunkX        the x coordinate of the chunk
     * @param chunkZ        the z coordinate of the chunk
     * @param dimensionType the dimension type of the chunk
     * @return the entities in the chunk
     */
    CompletableFuture<Map<Long, Entity>> readEntities(int chunkX, int chunkZ, DimensionType dimensionType);

    /**
     * Similar to {@link #readEntities(int, int, DimensionType)} but is synchronous.
     */
    Map<Long, Entity> readEntitiesSync(int chunkX, int chunkZ, DimensionType dimensionType);

    /**
     * Write the entities in a chunk.
     *
     * @param chunkX        the x coordinate of the chunk
     * @param chunkZ        the z coordinate of the chunk
     * @param dimensionType the dimension type of the chunk
     * @param entities      the entities to write. Note that the key is the unique id of the entity, not the runtime id
     *                      If an empty map is passed, all existing entities in the chunk will be removed.
     * @return a future which will be completed when the entities are written
     */
    CompletableFuture<Void> writeEntities(int chunkX, int chunkZ, DimensionType dimensionType, Map<Long, Entity> entities);

    /**
     * Similar to {@link #writeEntities(int, int, DimensionType, Map)} but is synchronous.
     */
    void writeEntitiesSync(int chunkX, int chunkZ, DimensionType dimensionType, Map<Long, Entity> entities);

    /**
     * Check if the storage contains a chunk.
     *
     * @param chunkX        the x coordinate of the chunk
     * @param chunkZ        the z coordinate of the chunk
     * @param dimensionType the dimension type of the chunk
     * @return {@code true} if the storage contains the chunk, {@code false} otherwise
     */
    boolean containChunk(int chunkX, int chunkZ, DimensionType dimensionType);

    /**
     * Write world data to the storage.
     *
     * @param worldData the world data to write
     */
    void writeWorldData(WorldData worldData);

    /**
     * Read world data from the storage.
     *
     * @return the world data
     */
    WorldData readWorldData();

    /**
     * Retrieves the name associated with the world storage. Which should be the same
     * registered in {@link Registries#WORLD_STORAGE_FACTORIES}.
     *
     * @return the name of the world storage
     */
    String getName();
}
