extends CharacterBody2D

# ---- Movement ----
var speed: float = 300.0
var jump_velocity: float = -450.0
var gravity: float = 1200.0

# ---- Health ----
var max_health: int = 100
var health: int = 100

# ---- Attack ----
var attack_range: float = 95.0
var attack_forward_only: bool = true
var attack_damage: int = 10
var attack_cooldown: float = 0.0
var attack_windup: float = 0.15
var attack_recover: float = 0.35
var is_attacking: bool = false

# ---- Block / stun ----
var is_blocking: bool = false
var block_damage_multiplier: float = 0.35
var hit_stun: float = 0.0

# ---- Facing / bounds ----
var facing_dir: int = 1
var min_x: float = 40.0
var max_x: float = 1240.0

# ---- Links ----
var enemy_ref: CharacterBody2D = null
func set_enemy(e: CharacterBody2D) -> void:
	enemy_ref = e

# ---- Visuals ----
var anim: AnimatedSprite2D = null

# assets folder
const SPRITE_FOLDER := "res://assets/player"

func _ready() -> void:
	await get_tree().process_frame

	health = max_health
	velocity = Vector2.ZERO

	# 1) atrod vai izveido AnimatedSprite2D
	anim = _get_or_create_anim()

	# 2) ja nav sprite_frames, uzbūvē no mapes
	if anim.sprite_frames == null or anim.sprite_frames.get_animation_names().is_empty():
		anim.sprite_frames = _build_sprite_frames_from_folder(SPRITE_FOLDER)

	anim.visible = true
	anim.modulate.a = 1.0
	if anim.scale == Vector2.ZERO:
		anim.scale = Vector2.ONE

	_safe_play("idle")

func _physics_process(delta: float) -> void:
	# timers
	if attack_cooldown > 0.0:
		attack_cooldown = max(attack_cooldown - delta, 0.0)
	if hit_stun > 0.0:
		hit_stun = max(hit_stun - delta, 0.0)

	# blocking input
	is_blocking = Input.is_action_pressed("block") and hit_stun <= 0.0 and attack_cooldown <= 0.0

	# gravity + jump
	if not is_on_floor():
		velocity.y += gravity * delta
	else:
		if velocity.y > 0.0:
			velocity.y = 0.0

	if Input.is_action_just_pressed("jump") and is_on_floor() and hit_stun <= 0.0 and not is_blocking:
		velocity.y = jump_velocity

	# movement (disabled on stun/block)
	var dir: int = 0
	if hit_stun <= 0.0 and not is_blocking:
		if Input.is_action_pressed("move_left"):
			dir -= 1
		if Input.is_action_pressed("move_right"):
			dir += 1

	if dir != 0:
		facing_dir = dir

	velocity.x = float(dir) * speed
	move_and_slide()
	global_position.x = clamp(global_position.x, min_x, max_x)

	# flip
	if anim != null:
		anim.flip_h = (facing_dir == -1)

	# attack input
	if Input.is_action_just_pressed("attack") and attack_cooldown <= 0.0 and hit_stun <= 0.0 and not is_blocking:
		_start_attack()
	# animations
	if is_attacking:
		return 
		
	if is_blocking:
		_safe_play("block")
	elif hit_stun > 0.0:
		_safe_play("hit")
	elif not is_on_floor():
		_safe_play("jump")
	else:
		if abs(velocity.x) > 5.0:
			_safe_play("walk")
		else:
			_safe_play("idle")

func _start_attack() -> void:
	is_attacking = true
	attack_cooldown = attack_windup + attack_recover
	_safe_play("attack")
	
	var damage_timer := get_tree().create_timer(attack_windup)
	damage_timer.timeout.connect(func():
		_try_apply_damage()
	)
	
	var end_timer := get_tree().create_timer(attack_windup + attack_recover)
	end_timer.timeout.connect(func():
		is_attacking = false
	)
	#attack_cooldown = attack_windup + attack_recover
	#_safe_play("attack")

	#var t := get_tree().create_timer(attack_windup)
	#t.timeout.connect(func(): _try_apply_damage())

func _try_apply_damage() -> void:
	if enemy_ref == null:
		return
	if hit_stun > 0.0 or is_blocking:
		return

	var dx: float = enemy_ref.global_position.x - global_position.x
	var dist: float = abs(dx)

	if dist > attack_range:
		return

	if attack_forward_only:
		if facing_dir == 1 and dx < 0.0:
			return
		if facing_dir == -1 and dx > 0.0:
			return

	if enemy_ref.has_method("take_damage"):
		enemy_ref.call("take_damage", attack_damage, global_position.x)

func take_damage(amount: int, _attacker_x: float = 0.0) -> void:
	if health <= 0:
		return

	var final_amount := amount
	if is_blocking:
		final_amount = int(round(float(amount) * block_damage_multiplier))

	health = max(health - final_amount, 0)

	if not is_blocking:
		hit_stun = 0.20

	_safe_play("hit")

	if health <= 0:
		var parent_node := get_parent()
		if parent_node != null and parent_node.has_method("end_fight"):
			parent_node.call("end_fight", false)

# ---------------- Sprite helpers ----------------

func _get_or_create_anim() -> AnimatedSprite2D:

	var direct := get_node_or_null("AnimatedSprite2D")
	if direct != null and direct is AnimatedSprite2D:
		return direct as AnimatedSprite2D

	var found := _find_first_animated_sprite(self)
	if found != null:
		return found

	var new_anim := AnimatedSprite2D.new()
	new_anim.name = "AnimatedSprite2D"
	add_child(new_anim)
	new_anim.position = Vector2(0, -40)
	new_anim.scale = Vector2(0.25, 0.25)
	return new_anim

func _find_first_animated_sprite(root: Node) -> AnimatedSprite2D:
	if root is AnimatedSprite2D:
		return root
	for c in root.get_children():
		var res := _find_first_animated_sprite(c)
		if res != null:
			return res
	return null

func _safe_play(anim_name: String) -> void:
	if anim == null or anim.sprite_frames == null:
		return

	if not anim.sprite_frames.has_animation(anim_name):
		if anim_name != "idle" and anim.sprite_frames.has_animation("idle"):
			if anim.animation != "idle":
				anim.play("idle")
		return

	if anim.animation != anim_name:
		anim.play(anim_name)

func _build_sprite_frames_from_folder(folder: String) -> SpriteFrames:
	var frames := SpriteFrames.new()

	_add_anim_from_prefix(frames, folder, "idle", 6.0, true)
	_add_anim_from_prefix(frames, folder, "walk", 10.0, true)
	_add_anim_from_prefix(frames, folder, "attack", 12.0, false)
	_add_anim_from_prefix(frames, folder, "hit", 10.0, false)
	_add_anim_from_prefix(frames, folder, "block", 8.0, true)
	_add_anim_from_prefix(frames, folder, "jump", 8.0, false)

	if frames.get_animation_names().is_empty():
		push_warning("Player: No sprites found in " + folder + " (expected idle_1.png, walk_1.png etc.)")

	return frames

func _add_anim_from_prefix(frames: SpriteFrames, folder: String, anim_name: String, fps: float, loop: bool) -> void:
	var textures: Array[Texture2D] = _load_textures_sorted(folder, anim_name + "_")
	if textures.is_empty():
		return

	if not frames.has_animation(anim_name):
		frames.add_animation(anim_name)
	frames.set_animation_speed(anim_name, fps)
	frames.set_animation_loop(anim_name, loop)

	for t in textures:
		frames.add_frame(anim_name, t)

func _load_textures_sorted(folder: String, prefix: String) -> Array[Texture2D]:
	var out: Array[Texture2D] = []
	var dir := DirAccess.open(folder)
	if dir == null:
		push_warning("Player: Folder not found: " + folder)
		return out

	dir.list_dir_begin()
	var file := dir.get_next()
	while file != "":
		if not dir.current_is_dir():
			var lower := file.to_lower()
			if lower.ends_with(".png") and file.begins_with(prefix):
				var tex := load(folder + "/" + file) as Texture2D
				if tex != null:
					out.append(tex)
		file = dir.get_next()
	dir.list_dir_end()

	out.sort_custom(func(a: Texture2D, b: Texture2D) -> bool:
		return _extract_suffix_number(a.resource_path, prefix) < _extract_suffix_number(b.resource_path, prefix)
	)
	return out

func _extract_suffix_number(path: String, prefix: String) -> int:
	var file := path.get_file()
	if not file.begins_with(prefix):
		return 0
	var s := file.replace(prefix, "").replace(".png", "")
	return int(s)
