import { type World, type EntityId, type Actor, type Vec2 } from "../core/types";
import { idx } from "./world/world-logic";

export class EntityManager {
  private grid: Map<number, EntityId[]> = new Map();
  private actors: Map<EntityId, Actor>;
  private world: World;
  private lastId: number = 0;

  constructor(world: World) {
    this.world = world;
    this.actors = world.actors;
    this.lastId = Math.max(0, ...this.actors.keys());
    this.rebuildGrid();
  }


  rebuildGrid() {
    this.grid.clear();
    for (const actor of this.actors.values()) {
      this.addToGrid(actor);
    }
  }

  private addToGrid(actor: Actor) {
    const i = idx(this.world, actor.pos.x, actor.pos.y);
    if (!this.grid.has(i)) {
      this.grid.set(i, []);
    }
    this.grid.get(i)!.push(actor.id);
  }

  private removeFromGrid(actor: Actor) {
    const i = idx(this.world, actor.pos.x, actor.pos.y);
    const ids = this.grid.get(i);
    if (ids) {
      const index = ids.indexOf(actor.id);
      if (index !== -1) {
        ids.splice(index, 1);
      }
      if (ids.length === 0) {
        this.grid.delete(i);
      }
    }
  }

  moveActor(actorId: EntityId, from: Vec2, to: Vec2) {
    const actor = this.actors.get(actorId);
    if (!actor) return;

    // Remove from old position
    const oldIdx = idx(this.world, from.x, from.y);
    const ids = this.grid.get(oldIdx);
    if (ids) {
      const index = ids.indexOf(actorId);
      if (index !== -1) ids.splice(index, 1);
      if (ids.length === 0) this.grid.delete(oldIdx);
    }

    // Update position
    actor.pos.x = to.x;
    actor.pos.y = to.y;

    // Add to new position
    const newIdx = idx(this.world, to.x, to.y);
    if (!this.grid.has(newIdx)) this.grid.set(newIdx, []);
    this.grid.get(newIdx)!.push(actorId);
  }

  addActor(actor: Actor) {
    this.actors.set(actor.id, actor);
    this.addToGrid(actor);
  }

  removeActor(actorId: EntityId) {
    const actor = this.actors.get(actorId);
    if (actor) {
      this.removeFromGrid(actor);
      this.actors.delete(actorId);
    }
  }



  getActorsAt(x: number, y: number): Actor[] {
    const i = idx(this.world, x, y);
    const ids = this.grid.get(i);
    if (!ids) return [];
    return ids.map(id => this.actors.get(id)!).filter(Boolean);
  }

  isOccupied(x: number, y: number, ignoreType?: string): boolean {
    const actors = this.getActorsAt(x, y);
    if (ignoreType) {
      return actors.some(a => a.type !== ignoreType);
    }
    return actors.length > 0;
  }

  getNextId(): EntityId {
    this.lastId++;
    return this.lastId;
  }
}

