class_name NYXPlayer
extends CharacterBody2D

signal stats_changed(health: float, max_health: float, shadow: float, max_shadow: float)
signal cast_night_lash(origin: Vector2, direction: Vector2)
signal cast_crown_of_dread(origin: Vector2)
signal cast_starfall(origin: Vector2, target: Vector2)
signal dash_started(origin: Vector2, direction: Vector2)
signal took_damage
signal cooldowns_changed(cooldowns: Dictionary)
signal ability_failed(reason: String)
signal died

@export var speed: float = 340.0
@export var dash_speed: float = 1120.0
@export var dash_duration: float = 0.16
@export var dash_cooldown: float = 0.55
@export var attack_cooldown: float = 0.18
@export var crown_cooldown: float = 3.8
@export var starfall_cooldown: float = 4.8
@export var max_health: float = 220.0
@export var max_shadow: float = 100.0
@export var dash_shadow_cost: float = 18.0
@export var crown_shadow_cost: float = 28.0
@export var starfall_shadow_cost: float = 38.0

@onready var aim_line: Line2D = $AimLine
@onready var cloak: Polygon2D = $ShadowAura

var health: float = max_health
var shadow: float = max_shadow
var facing: Vector2 = Vector2.RIGHT
var dash_time: float = 0.0
var dash_timer: float = 0.0
var invulnerable_time: float = 0.0
var attack_timer: float = 0.0
var crown_timer: float = 0.0
var starfall_timer: float = 0.0

func _ready() -> void:
	health = max_health
	shadow = max_shadow
	_emit_stats()
	_emit_cooldowns()

func _physics_process(delta: float) -> void:
	var input: Vector2 = _get_keyboard_movement()
	if input.length() > 0.01:
		facing = input.normalized()

	var mouse_direction: Vector2 = (get_global_mouse_position() - global_position).normalized()
	if mouse_direction.length() > 0.01:
		aim_line.points = PackedVector2Array([Vector2.ZERO, mouse_direction * 82.0])
		rotation = lerp_angle(rotation, mouse_direction.angle(), 12.0 * delta)

	dash_timer = maxf(0.0, dash_timer - delta)
	dash_time = maxf(0.0, dash_time - delta)
	invulnerable_time = maxf(0.0, invulnerable_time - delta)
	attack_timer = maxf(0.0, attack_timer - delta)
	crown_timer = maxf(0.0, crown_timer - delta)
	starfall_timer = maxf(0.0, starfall_timer - delta)
	shadow = minf(max_shadow, shadow + 18.0 * delta)

	var current_speed: float = dash_speed if dash_time > 0.0 else speed
	var movement_direction: Vector2 = input
	if dash_time > 0.0 and movement_direction.length() < 0.01:
		movement_direction = facing
	velocity = movement_direction * current_speed
	move_and_slide()
	cloak.modulate.a = 1.0 if dash_time > 0.0 else 0.82
	_emit_stats()
	_emit_cooldowns()

func _input(event: InputEvent) -> void:
	if event.is_action_pressed("attack"):
		if attack_timer > 0.0:
			ability_failed.emit("Night Lash is recovering.")
			return
		var direction: Vector2 = (get_global_mouse_position() - global_position).normalized()
		if direction.length() < 0.01:
			direction = facing
		facing = direction
		attack_timer = attack_cooldown
		cast_night_lash.emit(global_position, direction)
		get_viewport().set_input_as_handled()
	elif event.is_action_pressed("dash"):
		_try_dash()
		get_viewport().set_input_as_handled()
	elif event.is_action_pressed("crown_of_dread"):
		_try_crown_of_dread()
		get_viewport().set_input_as_handled()
	elif event.is_action_pressed("starfall"):
		_try_starfall()
		get_viewport().set_input_as_handled()

func apply_damage(amount: float) -> void:
	if invulnerable_time > 0.0 or health <= 0.0:
		return
	health = maxf(0.0, health - amount)
	invulnerable_time = 0.42
	took_damage.emit()
	modulate = Color(1.0, 0.6, 0.72)
	var tween: Tween = create_tween()
	tween.tween_property(self, "modulate", Color.WHITE, 0.16)
	_emit_stats()
	if health <= 0.0:
		died.emit()

func restore_shadow(amount: float) -> void:
	shadow = minf(max_shadow, shadow + amount)
	_emit_stats()

func increase_max_health(amount: float, heal_amount: float) -> void:
	max_health += amount
	health = minf(max_health, health + heal_amount)
	_emit_stats()

func _try_dash() -> void:
	if dash_timer > 0.0:
		ability_failed.emit("Eclipse Step is recovering.")
		return
	if not _spend_shadow(dash_shadow_cost):
		ability_failed.emit("Need more Shadow for Eclipse Step.")
		return
	dash_time = dash_duration
	dash_timer = dash_cooldown
	invulnerable_time = dash_duration + 0.08
	dash_started.emit(global_position, facing)
	_emit_cooldowns()

func _try_crown_of_dread() -> void:
	if crown_timer > 0.0:
		ability_failed.emit("Crown of Dread is recovering.")
		return
	if not _spend_shadow(crown_shadow_cost):
		ability_failed.emit("Need more Shadow for Crown of Dread.")
		return
	crown_timer = crown_cooldown
	cast_crown_of_dread.emit(global_position)
	_emit_cooldowns()

func _try_starfall() -> void:
	if starfall_timer > 0.0:
		ability_failed.emit("Starfall is recovering.")
		return
	if not _spend_shadow(starfall_shadow_cost):
		ability_failed.emit("Need more Shadow for Starfall.")
		return
	starfall_timer = starfall_cooldown
	cast_starfall.emit(global_position, get_global_mouse_position())
	_emit_cooldowns()

func _spend_shadow(amount: float) -> bool:
	if shadow < amount:
		return false
	shadow -= amount
	_emit_stats()
	return true

func _get_keyboard_movement() -> Vector2:
	var x_axis: float = 0.0
	var y_axis: float = 0.0

	if Input.is_action_pressed("move_right") or Input.is_key_pressed(KEY_RIGHT) or Input.is_key_pressed(KEY_D):
		x_axis += 1.0
	if Input.is_action_pressed("move_left") or Input.is_key_pressed(KEY_LEFT) or Input.is_key_pressed(KEY_A):
		x_axis -= 1.0
	if Input.is_action_pressed("move_down") or Input.is_key_pressed(KEY_DOWN) or Input.is_key_pressed(KEY_S):
		y_axis += 1.0
	if Input.is_action_pressed("move_up") or Input.is_key_pressed(KEY_UP) or Input.is_key_pressed(KEY_W):
		y_axis -= 1.0

	var direction: Vector2 = Vector2(x_axis, y_axis)
	return direction.normalized() if direction.length() > 1.0 else direction

func _emit_stats() -> void:
	stats_changed.emit(health, max_health, shadow, max_shadow)

func _emit_cooldowns() -> void:
	cooldowns_changed.emit({
		"Night Lash": {
			"cooldown": attack_timer,
			"max": attack_cooldown,
			"cost": 0.0,
		},
		"Eclipse Step": {
			"cooldown": dash_timer,
			"max": dash_cooldown,
			"cost": dash_shadow_cost,
		},
		"Crown": {
			"cooldown": crown_timer,
			"max": crown_cooldown,
			"cost": crown_shadow_cost,
		},
		"Starfall": {
			"cooldown": starfall_timer,
			"max": starfall_cooldown,
			"cost": starfall_shadow_cost,
		},
	})
