package com.zombie.game.ecs;

import com.zombie.game.ecs.components.*;
import com.zombie.game.model.Constants;
import com.zombie.game.model.GameMap;
import lombok.Getter;

import java.util.*;

/** ECS world that manages all entities, components, and systems / ECS世界，管理所有实体、组件和系统 */
@Getter
public class ECSWorld {
    private final Object lock = new Object();
    private final GameMap map;
    private int nextEntityId;
    private final Set<Integer> entities = new LinkedHashSet<>();
    private final Set<Integer> players = new LinkedHashSet<>();
    private final Set<Integer> zombies = new LinkedHashSet<>();
    private final Map<Integer, Position> positions = new HashMap<>();
    private final Map<Integer, Health> healths = new HashMap<>();
    private final Map<Integer, Collision> collisions = new HashMap<>();
    private final Map<Integer, RenderInfo> renderInfos = new HashMap<>();
    private final Map<Integer, PlayerInput> playerInputs = new HashMap<>();
    private final Map<Integer, BlocksMovement> blocksMovements = new HashMap<>();
    private final Map<Integer, MovementCost> movementCosts = new HashMap<>();
    private final Map<Integer, ZombieAI> zombieAIs = new HashMap<>();
    private final List<System> systems = new ArrayList<>();
    private float gameTime;
    private final Map<String, Integer> playerIdToEntity = new HashMap<>();

    public ECSWorld(String mapFilePath) {
        this.map = new GameMap(mapFilePath);
        this.nextEntityId = 0;
        this.gameTime = 0;

        // Wire GameMap to ECS component maps
        map.setComponentMaps(blocksMovements, movementCosts);

        // Create wall entities from map data
        createWallsFromMapData();
    }

    /** Create static wall and nut wall entities from map JSON data */
    private void createWallsFromMapData() {
        for (int[] pos : map.getStaticWallData()) {
            createStaticWallEntity(pos[0], pos[1]);
        }
        for (int[] pos : map.getNutWallData()) {
            createNutWallEntity(pos[0], pos[1]);
        }
    }

    /** Create a static wall entity (indestructible, blocks movement) */
    public int createStaticWallEntity(int gx, int gy) {
        int entityId = createEntity();
        positions.put(entityId, new Position(gx + 0.5f, gy + 0.5f));
        blocksMovements.put(entityId, new BlocksMovement());
        map.registerWall(gx, gy, entityId);
        return entityId;
    }

    /** Create a nut wall entity (destructible, blocks movement with cost) */
    public int createNutWallEntity(int gx, int gy) {
        int entityId = createEntity();
        positions.put(entityId, new Position(gx + 0.5f, gy + 0.5f));
        healths.put(entityId, new Health(Constants.NUT_WALL_MAX_HEALTH));
        blocksMovements.put(entityId, new BlocksMovement());
        movementCosts.put(entityId, new MovementCost(Constants.NUT_WALL_MOVEMENT_COST));
        map.registerWall(gx, gy, entityId);
        return entityId;
    }

    /** Create a zombie entity for testing pathfinding */
    public int createZombieEntity(float x, float y) {
        int entityId = createEntity();
        positions.put(entityId, new Position(x, y));
        healths.put(entityId, new Health(100));
        collisions.put(entityId, new Collision(Constants.ZOMBIE_SIZE));
        renderInfos.put(entityId, new RenderInfo(RenderInfo.EntityType.ZOMBIE));
        zombieAIs.put(entityId, new ZombieAI());
        zombies.add(entityId);
        return entityId;
    }

    public int createEntity() {
        int id = nextEntityId++;
        entities.add(id);
        return id;
    }

    public void destroyEntity(int id) {
        entities.remove(id);
        players.remove(id);
        zombies.remove(id);
        positions.remove(id);
        healths.remove(id);
        collisions.remove(id);
        renderInfos.remove(id);
        playerInputs.remove(id);
        blocksMovements.remove(id);
        movementCosts.remove(id);
        zombieAIs.remove(id);
    }

    public int createPlayerEntity(String playerId, String name, float x, float y) {
        int entityId = createEntity();
        positions.put(entityId, new Position(x, y));
        healths.put(entityId, new Health(100));
        collisions.put(entityId, new Collision(Constants.PLAYER_SIZE));
        renderInfos.put(entityId, new RenderInfo(RenderInfo.EntityType.PLAYER));
        playerInputs.put(entityId, new PlayerInput());
        players.add(entityId);
        playerIdToEntity.put(playerId, entityId);
        return entityId;
    }

    public Integer getPlayerEntity(String playerId) {
        return playerIdToEntity.get(playerId);
    }

    public int[][] getMapData() { return map.getCells(); }

    public void update(float dt) {
        gameTime += dt;
        for (System system : systems) {
            system.update(dt, this);
        }
    }

    public void addSystem(System system) {
        systems.add(system);
    }
}
