// gameObjects.ts - OPTIMIZED VERSION WITH SINGLE ENEMY DETECTION
import type { Bullet, Particle, Enemy, Vector, GameConfig } from "./types"
import { Pool } from "./objectPool"
import { SpatialGridSystem } from "./spatialGrid"
import {
  createVector,
  vectorOps,
  wrapPosition,
  random,
  hasOnlyTextualChildren,
} from "./utils"

// Factory functions for object pools
export const createBullet = (): Bullet => ({
  position: createVector(0, 0),
  velocity: createVector(0, 0),
  createdAt: 0,
  active: false,
})

export const createParticle = (): Particle => ({
  position: createVector(0, 0),
  velocity: createVector(0, 0),
  color: "#ffffff",
  life: 0,
  maxLife: 0,
  active: false,
})

// Reset functions for object pools
export const resetBullet = (bullet: Bullet): void => {
  bullet.position.x = 0
  bullet.position.y = 0
  bullet.velocity.x = 0
  bullet.velocity.y = 0
  bullet.createdAt = 0
}

export const resetParticle = (particle: Particle): void => {
  particle.position.x = 0
  particle.position.y = 0
  particle.velocity.x = 0
  particle.velocity.y = 0
  particle.color = "#ffffff"
  particle.life = 0
  particle.maxLife = 0
}

// Game object management class - OPTIMIZED WITH SINGLE ENEMY DETECTION
export class GameObjectManager {
  private bulletPool: Pool<Bullet>
  private particlePool: Pool<Particle>
  private spatialGrid: SpatialGridSystem
  private CONFIG: GameConfig

  // MODIFIED: Static enemy list - populated once at game start
  private staticEnemies: Enemy[] = []
  private enemiesInitialized: boolean = false

  constructor(config: GameConfig) {
    this.CONFIG = config

    // Initialize object pools
    this.bulletPool = new Pool(
      config.bullets.maxCount * 2, // Extra capacity for safety
      createBullet,
      resetBullet,
    )

    this.particlePool = new Pool(
      200, // Max particles
      createParticle,
      resetParticle,
    )

    // Initialize spatial grid
    this.spatialGrid = new SpatialGridSystem(config)
  }

  // Bullet management
  createBulletAt(position: Vector, velocity: Vector): boolean {
    const bullet = this.bulletPool.get()
    if (!bullet) return false

    bullet.position = vectorOps.copy(position)
    bullet.velocity = vectorOps.copy(velocity)
    bullet.createdAt = Date.now()

    return true
  }

  getActiveBullets(): Bullet[] {
    return this.bulletPool.getActiveObjects()
  }

  updateBullets(deltaTime: number): void {
    const currentTime = Date.now()
    const bullets = this.bulletPool.getActiveObjects()

    for (let i = 0; i < bullets.length; i++) {
      const bullet = bullets[i]

      // Check lifetime
      if (currentTime - bullet.createdAt > this.CONFIG.bullets.lifetime) {
        this.bulletPool.release(bullet)
        continue
      }

      // Update position
      bullet.position = wrapPosition(
        vectorOps.add(
          bullet.position,
          vectorOps.multiply(bullet.velocity, deltaTime),
        ),
        this.CONFIG,
      )
    }
  }

  // Particle management
  createParticlesAt(position: Vector, count: number = 8): void {
    for (let i = 0; i < count; i++) {
      const particle = this.particlePool.get()
      if (!particle) break

      particle.position = vectorOps.copy(position)
      particle.velocity = vectorOps.multiply(
        vectorOps.normalize(createVector(random(-1, 1), random(-1, 1))),
        random(50, this.CONFIG.particles.speed),
      )
      particle.color =
        this.CONFIG.particles.colors[
          Math.floor(random(0, this.CONFIG.particles.colors.length))
        ]
      particle.life = this.CONFIG.particles.lifetime
      particle.maxLife = this.CONFIG.particles.lifetime
    }
  }

  getActiveParticles(): Particle[] {
    return this.particlePool.getActiveObjects()
  }

  updateParticles(deltaTime: number): void {
    const particles = this.particlePool.getActiveObjects()

    for (let i = 0; i < particles.length; i++) {
      const particle = particles[i]

      // Update life
      particle.life -= deltaTime * 1000
      if (particle.life <= 0) {
        this.particlePool.release(particle)
        continue
      }

      // Update position and apply friction
      particle.position = vectorOps.add(
        particle.position,
        vectorOps.multiply(particle.velocity, deltaTime),
      )
      particle.velocity = vectorOps.multiply(particle.velocity, 0.98)
    }
  }

  // MODIFIED: One-time enemy initialization at game start
  initializeEnemiesOnce(): Enemy[] {
    if (this.enemiesInitialized) {
      return this.staticEnemies
    }

    if (typeof document === "undefined") {
      return this.staticEnemies
    }

    this.performInitialEnemyScan()
    this.enemiesInitialized = true

    console.log(
      `Initialized ${this.staticEnemies.length} enemies at game start`,
    )
    return this.staticEnemies
  }

  // MODIFIED: Initial enemy scan with exclusion of racleroids-ui elements
  private performInitialEnemyScan(): void {
    const elements = Array.from(document.querySelectorAll("*")) as HTMLElement[]
    const newEnemies: Enemy[] = []

    // Clear spatial grid
    this.spatialGrid.clear()

    // Find and add enemies to spatial grid
    for (let i = 0; i < elements.length; i++) {
      const el = elements[i]

      // MODIFIED: Exclude elements with specific conditions
      if (this.shouldExcludeElement(el)) {
        continue
      }

      if (
        !this.CONFIG.enemies.ignoredTags.has(el.tagName) &&
        !el.classList.contains("RACLEROIDS") &&
        hasOnlyTextualChildren(el, this.CONFIG)
      ) {
        el.classList.add("RACLEROIDSENEMY")
        const enemy: Enemy = {
          element: el,
          bounds: el.getBoundingClientRect(),
          gridX: 0,
          gridY: 0,
        }

        this.spatialGrid.addEnemy(enemy)
        newEnemies.push(enemy)
      }
    }

    this.staticEnemies = newEnemies
  }

  // MODIFIED: Check if element should be excluded from enemy detection
  private shouldExcludeElement(element: HTMLElement): boolean {
    // Exclude elements with id "racleroids-ui"
    if (element.id === "racleroids-ui") {
      return true
    }

    // Exclude children of elements with id "racleroids-ui"
    let parent = element.parentElement
    while (parent) {
      if (parent.id === "racleroids-ui") {
        return true
      }
      parent = parent.parentElement
    }

    // Exclude asteroids game elements
    if (element.classList.contains("RACLEROIDS")) {
      return true
    }

    // Exclude asteroids styles
    if (element.id === "racleroids-styles") {
      return true
    }

    return false
  }

  // REMOVED: updateEnemies method - no longer needed since enemies are static

  // Collision detection using spatial grid
  checkBulletCollisions(): {
    hitEnemies: Enemy[]
    newParticles: number
  } {
    const bullets = this.getActiveBullets()
    const hitEnemies: Enemy[] = []
    let totalNewParticles = 0

    for (let i = 0; i < bullets.length; i++) {
      const bullet = bullets[i]

      // Get nearby enemies using spatial grid
      const nearbyEnemies = this.spatialGrid.getEnemiesNearPoint(
        bullet.position,
        this.CONFIG.bullets.size,
      )

      let hit = false
      for (let j = 0; j < nearbyEnemies.length; j++) {
        const enemy = nearbyEnemies[j]

        // Skip if enemy element no longer exists in DOM
        if (!document.contains(enemy.element)) {
          this.spatialGrid.removeEnemy(enemy)
          continue
        }

        const bounds = enemy.bounds

        if (
          bullet.position.x >= bounds.left &&
          bullet.position.x <= bounds.right &&
          bullet.position.y >= bounds.top &&
          bullet.position.y <= bounds.bottom
        ) {
          hitEnemies.push(enemy)
          this.spatialGrid.removeEnemy(enemy)

          // Remove from static enemies list
          const index = this.staticEnemies.indexOf(enemy)
          if (index > -1) {
            this.staticEnemies.splice(index, 1)
          }

          this.createParticlesAt(bullet.position)
          totalNewParticles += this.CONFIG.particles.count
          this.bulletPool.release(bullet)
          hit = true
          break
        }
      }
    }

    return { hitEnemies, newParticles: totalNewParticles }
  }

  getAllEnemies(): Enemy[] {
    return this.staticEnemies
  }

  // Get count of remaining enemies
  getRemainingEnemyCount(): number {
    // Filter out enemies that no longer exist in DOM
    this.staticEnemies = this.staticEnemies.filter((enemy) =>
      document.contains(enemy.element),
    )
    return this.staticEnemies.length
  }

  // Cleanup
  cleanup(): void {
    this.bulletPool.releaseAll()
    this.particlePool.releaseAll()
    this.spatialGrid.clear()

    // Clear static enemies
    this.staticEnemies = []
    this.enemiesInitialized = false
  }
}
