extends Node2D

const SCREEN       = Vector2(640, 640)
const CENTER       = Vector2(320, 320)

const ShipScene      = preload("res://scenes/Ship.tscn")
const AsteroidScene  = preload("res://scenes/Asteroid.tscn")
const BulletScene    = preload("res://scenes/Bullet.tscn")
const UFOScene       = preload("res://scenes/UFO.tscn")
const ExplosionScene = preload("res://scenes/Explosion.tscn")
const PowerUpScene   = preload("res://scenes/PowerUp.tscn")

@onready var bullets_node:   Node2D        = $Bullets
@onready var asteroids_node: Node2D        = $Asteroids
@onready var effects_node:   Node2D        = $Effects
@onready var powerups_node:  Node2D        = $PowerUps
@onready var stars_node:     Node2D        = $Stars
@onready var hud_score:      Label         = $HUD/ScoreLabel
@onready var hud_hi:         Label         = $HUD/HiLabel
@onready var hud_lives:      HBoxContainer = $HUD/LivesBox
@onready var hud_wave:       Label         = $HUD/WaveLabel
@onready var msg_label:      Label         = $HUD/MsgLabel
@onready var ufo_timer:      Timer         = $UFOTimer
@onready var powerup_timer:  Timer         = $PowerUpTimer

var ship:           Area2D = null
var ufo:            Area2D = null
var _game_over:     bool   = false
var _transitioning: bool   = false


func _ready() -> void:
	_make_stars()
	GameState.score = 0
	GameState.lives = 3
	_start_wave()
	_refresh_hud()
	ufo_timer.timeout.connect(_spawn_ufo)
	powerup_timer.timeout.connect(_spawn_powerup)
	ufo_timer.wait_time     = randf_range(15, 25)
	powerup_timer.wait_time = randf_range(12, 20)
	ufo_timer.start()
	powerup_timer.start()
	_show_message("Wave %d" % GameState.wave, 1.5)


func _process(_delta: float) -> void:
	if _game_over:
		if Input.is_key_pressed(KEY_R):
			GameState.wave  = 1
			GameState.score = 0
			GameState.lives = 3
			get_tree().reload_current_scene()
		return
	if _transitioning:
		return
	_check_collisions()
	if asteroids_node.get_child_count() == 0:
		_next_wave()


# ── Wave management ──────────────────────────────────────────
func _start_wave() -> void:
	for c in bullets_node.get_children():  c.queue_free()
	for c in powerups_node.get_children(): c.queue_free()
	if is_instance_valid(ship):
		ship.queue_free()
	ship = ShipScene.instantiate()
	ship.position = CENTER
	ship.bullet_fired.connect(_on_ship_shoot)
	ship.ship_destroyed.connect(_on_ship_destroyed)
	add_child(ship)
	var count = 4 + (GameState.wave - 1) * 2
	for i in range(count):
		_spawn_asteroid(0, _safe_spawn_pos(), Vector2.ZERO)
	_refresh_hud()


func _next_wave() -> void:
	if _transitioning:
		return
	_transitioning = true
	GameState.wave += 1
	_show_message("Wave %d!" % GameState.wave, 2.0)
	await get_tree().create_timer(2.0).timeout
	_transitioning = false
	_start_wave()
	ufo_timer.wait_time = max(8.0, 20.0 - GameState.wave)
	ufo_timer.start()


func _safe_spawn_pos() -> Vector2:
	var angle = randf() * TAU
	var dist  = randf_range(160, 280)
	return CENTER + Vector2(cos(angle), sin(angle)) * dist


# ── Spawning ─────────────────────────────────────────────────
func _spawn_asteroid(size: int, pos: Vector2, inherit_vel: Vector2) -> void:
	var a = AsteroidScene.instantiate()
	asteroids_node.add_child(a)
	var spd_ranges = [40.0, 65.0, 95.0]
	var spd = randf_range(spd_ranges[size] * 0.6, spd_ranges[size] * 1.4)
	var angle = randf() * TAU
	var vel = inherit_vel + Vector2(cos(angle), sin(angle)) * spd
	a.setup(size, randi() % 3, vel)
	a.position = pos
	a.asteroid_destroyed.connect(_on_asteroid_destroyed)


func _spawn_ufo() -> void:
	if is_instance_valid(ufo) or _game_over:
		return
	ufo = UFOScene.instantiate()
	add_child(ufo)
	ufo.ufo_destroyed.connect(_on_ufo_destroyed)
	ufo.ufo_bullet_fired.connect(_on_ufo_shoot)
	ufo_timer.wait_time = randf_range(max(8.0, 20.0 - GameState.wave), 30.0)
	ufo_timer.start()


func _spawn_powerup() -> void:
	if _game_over:
		return
	var p = PowerUpScene.instantiate()
	powerups_node.add_child(p)
	var kinds = ["shield", "rapid"]
	p.setup(Vector2(randf_range(60, 580), randf_range(60, 580)), kinds[randi() % 2])
	powerup_timer.wait_time = randf_range(15, 25)
	powerup_timer.start()


# ── Shooting ─────────────────────────────────────────────────
func _on_ship_shoot(pos: Vector2, angle: float) -> void:
	var b = BulletScene.instantiate()
	b.is_enemy = false
	bullets_node.add_child(b)
	b.setup(pos, angle)


func _on_ufo_shoot(pos: Vector2, angle: float) -> void:
	var b = BulletScene.instantiate()
	b.is_enemy = true
	b.speed    = 260.0
	b.lifetime = 2.0
	bullets_node.add_child(b)
	b.setup(pos, angle)
	b.modulate = Color(1.0, 0.4, 0.4)


# ── Collision detection ───────────────────────────────────────
func _overlaps(a: Vector2, ra: float, b: Vector2, rb: float) -> bool:
	return a.distance_to(b) < ra + rb


func _check_collisions() -> void:
	var ship_alive = is_instance_valid(ship) and ship._alive
	var ship_pos   = ship.global_position if ship_alive else Vector2(-9999, -9999)

	for bullet in bullets_node.get_children():
		if not is_instance_valid(bullet):
			continue
		var bpos = bullet.global_position

		if not bullet.is_enemy:
			# vs asteroids
			for ast in asteroids_node.get_children():
				if not is_instance_valid(ast):
					continue
				var radii = [28.0, 17.0, 10.0]
				if _overlaps(bpos, 3, ast.global_position, radii[ast.size]):
					_add_score(ast.points)
					_spawn_explosion(ast.global_position, ast.size)
					ast.die()
					if is_instance_valid(bullet): bullet.queue_free()
					break
			# vs UFO
			if is_instance_valid(ufo) and ufo.alive:
				if _overlaps(bpos, 3, ufo.global_position, 20):
					_add_score(200)
					ufo.die()
					if is_instance_valid(bullet): bullet.queue_free()
		else:
			# vs ship
			if ship_alive and _overlaps(bpos, 3, ship_pos, 16):
				ship.die()
				if is_instance_valid(bullet): bullet.queue_free()

	# Asteroids vs ship
	if ship_alive:
		for ast in asteroids_node.get_children():
			if not is_instance_valid(ast):
				continue
			var radii = [28.0, 17.0, 10.0]
			if _overlaps(ship_pos, 14, ast.global_position, radii[ast.size]):
				ship.die()
				break

	# UFO vs ship
	if ship_alive and is_instance_valid(ufo) and ufo.alive:
		if _overlaps(ship_pos, 14, ufo.global_position, 20):
			ship.die()

	# Power-ups vs ship
	if ship_alive:
		for p in powerups_node.get_children():
			if not is_instance_valid(p):
				continue
			if _overlaps(ship_pos, 16, p.global_position, 12):
				_collect_powerup(p)


# ── Events ───────────────────────────────────────────────────
func _on_asteroid_destroyed(size: int, pos: Vector2, _variant: int) -> void:
	if size < 2:
		for i in range(2):
			var offset = Vector2(randf_range(-12, 12), randf_range(-12, 12))
			_spawn_asteroid(size + 1, pos + offset, Vector2.ZERO)


func _on_ufo_destroyed(pos: Vector2) -> void:
	_spawn_explosion(pos, 0)
	_refresh_hud()


func _on_ship_destroyed() -> void:
	_spawn_explosion(ship.position, 1)
	GameState.lives -= 1
	_refresh_hud()
	if GameState.lives <= 0:
		await get_tree().create_timer(1.5).timeout
		_show_message("GAME OVER\nPress R to restart")
		_game_over = true
	else:
		await get_tree().create_timer(2.0).timeout
		if not _game_over and is_instance_valid(ship):
			ship.respawn(CENTER)


func _collect_powerup(p: Node2D) -> void:
	match p.type:
		"shield": ship.apply_shield(6.0); _show_message("SHIELD!", 1.2)
		"rapid":  ship.apply_rapid(6.0);  _show_message("RAPID FIRE!", 1.2)
	p.queue_free()


# ── Helpers ──────────────────────────────────────────────────
func _add_score(pts: int) -> void:
	GameState.score += pts
	GameState.hi_score = max(GameState.score, GameState.hi_score)
	_refresh_hud()


func _spawn_explosion(pos: Vector2, size: int) -> void:
	var expl = ExplosionScene.instantiate()
	expl.position = pos
	expl.scale    = Vector2.ONE * [2.0, 1.4, 0.9][size]
	effects_node.add_child(expl)


func _refresh_hud() -> void:
	hud_score.text = "SCORE  %06d" % GameState.score
	hud_hi.text    = "HI  %06d"   % GameState.hi_score
	hud_wave.text  = "WAVE %d"    % GameState.wave
	for c in hud_lives.get_children(): c.queue_free()
	for i in range(clampi(GameState.lives, 0, 5)):
		var lbl = Label.new()
		lbl.text = "▲"
		lbl.add_theme_font_size_override("font_size", 18)
		lbl.add_theme_color_override("font_color", Color(0.4, 0.8, 1.0, 1.0))
		hud_lives.add_child(lbl)


func _show_message(text: String, duration: float = 0.0) -> void:
	msg_label.text    = text
	msg_label.visible = true
	if duration > 0:
		await get_tree().create_timer(duration).timeout
		if is_instance_valid(msg_label):
			msg_label.visible = false


func _make_stars() -> void:
	for i in range(80):
		var star  = ColorRect.new()
		var s     = randf_range(0.5, 2.0)
		star.size = Vector2(s, s)
		star.position = Vector2(randf_range(0, 640), randf_range(0, 640))
		var bright = randf_range(0.4, 1.0)
		star.color = Color(bright, bright, bright * 1.1, randf_range(0.5, 1.0))
		stars_node.add_child(star)
