# Player.gd
# Steuert den Spielercharakter: Bewegung, Kamera, HP, Angriff, Zielauswahl
extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5
const GRAVITY = 9.8

# Charakter-Klasse und Level-System
@export var character_class: CharacterClass
var level: int = 1
var current_xp: int = 0
var xp_to_next_level: int = 100  # XP benötigt für Level 2

# Aktuelle Stats (berechnet aus Klasse + Level)
var strength: int = 10
var agility: int = 10
var intelligence: int = 10
var stamina: int = 10
var armor: int = 0  # Rüstung aus Ausrüstung

# Level-Differenz Konstanten
const LEVEL_DIFF_DAMAGE_MOD = 0.1  # 10% mehr/weniger Schaden pro Level-Differenz
const MAX_LEVEL_DIFF_MOD = 0.5  # Maximal 50% Modifikation

var max_hp = 100
var current_hp = 100
var max_resource = 0       # Klassen-Ressource (Mana/Energie/Wut), 0 = keine
var current_resource = 0
var target = null          # Aktuell markierter Gegner

# Aktionsleiste: Skills (String) oder Consumables in Slots (0-8)
var action_bar_items: Array = [null, null, null, null, null, null, null, null, null]

# Alle verfügbaren Skills (für Fähigkeiten-Panel) — wird klassenabhängig befüllt
var available_skills: Array = []

# Skill-Definitionen pro Klasse
const AUTOATTACK_SKILL = {"id": "autoattack", "name": "Autoattack", "icon": "res://icons/autoattack_icon.svg", "description": "Greift das Ziel im Nahkampf an.\nSchaden: Waffenschaden + Main-Stat"}
const MELEE_SKILLS = [
	{"id": "heavy_strike", "name": "Heavy Strike", "icon": "res://icons/heavy_strike_icon.svg", "description": "Starker Hieb mit 3s Cooldown.\nSchaden: 10-15 + Main-Stat\nReichweite: 4.0"},
]
const MAGE_SKILLS = [
	{"id": "wand", "name": "Zauberstab", "icon": "res://icons/wand_icon.svg", "description": "Magischer Fernkampfangriff.\nSchaden: Waffenschaden + INT\nReichweite: 20.0\nIgnoriert Rüstung\nDeaktiviert Autoattack"},
	{"id": "frostbolt", "name": "Frostblitz", "icon": "res://icons/frostbolt_icon.svg", "description": "Magischer Fernkampfangriff mit Castzeit.\nSchaden: 12-20 + INT\nManakosten: 20\nReichweite: 20.0\nCastzeit: 1.5s\nCooldown: 2.5s"},
]
var potion_cooldown: float = 0.0
const POTION_COOLDOWN_TIME = 1.0

# Equipment System
var equipment: Dictionary = {
	Equipment.Slot.HEAD: null,
	Equipment.Slot.CHEST: null,
	Equipment.Slot.HANDS: null,
	Equipment.Slot.LEGS: null,
	Equipment.Slot.FEET: null,
	Equipment.Slot.WEAPON: null,
	Equipment.Slot.OFFHAND: null
}

# Inventar System
var inventory: Inventory = Inventory.new()

# Global Cooldown System (GCD) - gilt für alle Aktionen inkl. Autoattack
var global_cooldown = 0.0
const BASE_GCD = 1.5  # Basis-GCD in Sekunden (wird durch Haste modifiziert)
var haste: float = 0.0  # Angriffsgeschwindigkeits-Bonus (0.1 = 10% schneller)

# Autoattack System
var autoattack_active = false  # Ob Autoattack aktiv ist

# Zauberstab System (Magier-Fernkampf, exklusiv mit Autoattack)
var wand_active = false
const WAND_RANGE = 20.0

# Skills System - individuelle Cooldowns (zusätzlich zum GCD)
var heavy_strike_cooldown = 0.0
const HEAVY_STRIKE_DAMAGE_MIN = 10
const HEAVY_STRIKE_DAMAGE_MAX = 15
const HEAVY_STRIKE_COOLDOWN = 3.0
const HEAVY_STRIKE_RANGE = 4.0

var frostbolt_cooldown = 0.0
const FROSTBOLT_DAMAGE_MIN = 12
const FROSTBOLT_DAMAGE_MAX = 20
const FROSTBOLT_COOLDOWN = 2.5
const FROSTBOLT_RANGE = 20.0
const FROSTBOLT_MANA_COST = 20
const FROSTBOLT_CAST_TIME = 1.5

# Cast-System
var is_casting = false
var cast_time_remaining = 0.0
var cast_time_total = 0.0
var cast_spell_id = ""  # Welcher Zauber gecastet wird

# Animation System
const ANIMATION_FILES = {
	"walk": "res://assets/animations/Walking.fbx",
	"walk_back": "res://assets/animations/Walking Backwards.fbx",
	"strafe_left": "res://assets/animations/Left Strafe Walking.fbx",
	"strafe_right": "res://assets/animations/Right Strafe Walking.fbx",
	"jump": "res://assets/animations/Jumping.fbx",
	"autoattack": "res://assets/animations/Autoattack.fbx",
	"heavy_strike": "res://assets/animations/Heavy Strike.fbx",
	"die": "res://assets/animations/Dying Backwards.fbx",
}
var anim_player: AnimationPlayer = null
var current_anim: String = ""

@onready var camera_pivot = $CameraPivot
@onready var camera = $CameraPivot/Camera3D
@onready var hud = $HUD
@onready var character_panel = $CharacterPanel
@onready var inventory_panel = $InventoryPanel
@onready var loot_window = $LootWindow
@onready var skill_panel = $SkillPanel

func _ready():
	# Stats aus Klasse berechnen
	_calculate_stats()
	current_hp = max_hp
	current_resource = max_resource

	hud.update_health(current_hp, max_hp)
	hud.update_resource(current_resource, max_resource, get_resource_name())
	hud.update_level(level, current_xp, xp_to_next_level)
	hud.set_active_slot(0)
	# Skills klassenabhängig aufbauen
	_init_class_skills()
	# Aktionsleiste initialisieren (Skills + Items)
	for i in range(9):
		_refresh_action_slot(i)

	# HUD-Klicks und Drag verbinden
	hud.slot_clicked.connect(_on_slot_clicked)
	hud.slot_drag_removed.connect(_on_slot_drag_removed)
	hud.slot_drag_swapped.connect(_on_slot_drag_swapped)

	# Inventar Panel initialisieren
	inventory_panel.setup(self)

	# Loot Window initialisieren
	loot_window.setup(self)

	# Skill Panel initialisieren
	skill_panel.setup(self)

	# Gold im HUD aktualisieren wenn sich Gold ändert
	inventory.gold_changed.connect(func(amount): hud.update_gold(amount))

	# Animationen laden
	_setup_animations()

# Animationen aus FBX-Dateien laden und in AnimationPlayer einbinden
func _setup_animations():
	print("Player Kinder: ")
	for child in get_children():
		print("  - ", child.name, " (", child.get_class(), ")")
	var model = get_node_or_null("PlayerModel")
	if model == null:
		print("PlayerModel nicht gefunden!")
		return

	# AnimationPlayer im Modell finden
	anim_player = _find_node_by_class(model, "AnimationPlayer")
	if anim_player == null:
		# Neuen AnimationPlayer erstellen und ans Modell hängen
		anim_player = AnimationPlayer.new()
		anim_player.name = "AnimationPlayer"
		model.add_child(anim_player)
		print("Neuer AnimationPlayer erstellt")

	# AnimationLibrary holen oder erstellen
	var lib: AnimationLibrary
	if anim_player.has_animation_library(""):
		lib = anim_player.get_animation_library("")
	else:
		lib = AnimationLibrary.new()
		anim_player.add_animation_library("", lib)

	# Animationen aus separaten FBX-Dateien laden
	for anim_id in ANIMATION_FILES:
		var scene = load(ANIMATION_FILES[anim_id]) as PackedScene
		if scene == null:
			print("Animation nicht gefunden: ", ANIMATION_FILES[anim_id])
			continue

		var instance = scene.instantiate()
		var source_ap = _find_node_by_class(instance, "AnimationPlayer")

		if source_ap:
			var names = source_ap.get_animation_list()
			if names.size() > 0:
				var anim = source_ap.get_animation(names[0])
				if lib.has_animation(anim_id):
					lib.remove_animation(anim_id)
				lib.add_animation(anim_id, anim)
				print("Animation geladen: ", anim_id)

		instance.queue_free()

	print("Verfügbare Animationen: ", anim_player.get_animation_list())

# Rekursiv nach einem Node einer bestimmten Klasse suchen
func _find_node_by_class(node: Node, class_name_str: String) -> Node:
	for child in node.get_children():
		if child.get_class() == class_name_str:
			return child
		var result = _find_node_by_class(child, class_name_str)
		if result:
			return result
	return null

# Animation basierend auf Bewegungszustand abspielen
func _update_animation(input_dir: Vector2):
	if anim_player == null:
		return

	var new_anim = ""

	if not is_on_floor():
		new_anim = "jump"
	elif input_dir.length() > 0:
		# Vorwärts/Rückwärts hat Priorität
		if input_dir.y < -0.1:
			new_anim = "walk"
		elif input_dir.y > 0.1:
			new_anim = "walk_back"
		elif input_dir.x < -0.1:
			new_anim = "strafe_left"
		elif input_dir.x > 0.1:
			new_anim = "strafe_right"
		else:
			new_anim = "walk"
	else:
		new_anim = ""  # Idle — keine Animation (oder Idle wenn vorhanden)

	# Angriffs-/Death-Animation nicht unterbrechen
	if anim_player.is_playing() and current_anim in ["autoattack", "heavy_strike", "die"]:
		return

	if new_anim != current_anim:
		current_anim = new_anim
		if new_anim != "" and anim_player.has_animation(new_anim):
			anim_player.play(new_anim)
		else:
			anim_player.stop()

# Einmalige Animation abspielen (Angriff, Tod, etc.)
func _play_attack_anim(anim_name: String):
	if anim_player == null:
		return
	if anim_player.has_animation(anim_name):
		current_anim = anim_name
		anim_player.stop()
		anim_player.play(anim_name)

# Stats basierend auf Klasse und Level berechnen
func _calculate_stats():
	if character_class == null:
		# Fallback ohne Klasse
		strength = 10
		agility = 10
		intelligence = 10
		stamina = 10
		max_hp = 100
		max_resource = 0
		return

	# Stats = Basis + (Level-1) * Zuwachs pro Level
	var levels_gained = level - 1
	strength = character_class.base_strength + int(levels_gained * character_class.strength_per_level)
	agility = character_class.base_agility + int(levels_gained * character_class.agility_per_level)
	intelligence = character_class.base_intelligence + int(levels_gained * character_class.intelligence_per_level)
	stamina = character_class.base_stamina + int(levels_gained * character_class.stamina_per_level)

	# HP aus Stamina berechnen
	max_hp = stamina * CharacterClass.HP_PER_STAMINA
	# Klassen-Ressource berechnen
	_calculate_resource()

	# Equipment-Boni hinzufügen
	_apply_equipment_stats()

	print("Stats berechnet - STR: ", strength, " AGI: ", agility, " INT: ", intelligence, " STA: ", stamina, " ARM: ", armor, " HP: ", max_hp, " RES: ", max_resource)

# Skills klassenabhängig aufbauen
func _init_class_skills():
	available_skills = [AUTOATTACK_SKILL.duplicate()]
	if character_class and character_class.resource_type == CharacterClass.ResourceType.MANA:
		# Magier: Autoattack + Zauberstab + Frostblitz
		available_skills.append_array(MAGE_SKILLS.duplicate(true))
		action_bar_items[0] = "wand"
		action_bar_items[1] = "frostbolt"
		action_bar_items[2] = "autoattack"
	else:
		# Krieger/Schurke: Autoattack + Heavy Strike
		available_skills.append_array(MELEE_SKILLS.duplicate(true))
		action_bar_items[0] = "autoattack"
		action_bar_items[1] = "heavy_strike"

# Klassen-Ressource berechnen (Mana aus INT, Energie fix, Wut fix)
func _calculate_resource():
	if character_class == null or character_class.resource_type == CharacterClass.ResourceType.NONE:
		max_resource = 0
		return
	match character_class.resource_type:
		CharacterClass.ResourceType.MANA:
			max_resource = character_class.base_resource + intelligence * CharacterClass.MANA_PER_INT
		CharacterClass.ResourceType.ENERGY:
			max_resource = character_class.base_resource  # Fix, skaliert nicht
		CharacterClass.ResourceType.RAGE:
			max_resource = character_class.base_resource  # Fix, skaliert nicht

# Name der Klassen-Ressource
func get_resource_name() -> String:
	if character_class == null:
		return ""
	match character_class.resource_type:
		CharacterClass.ResourceType.MANA: return "Mana"
		CharacterClass.ResourceType.ENERGY: return "Energie"
		CharacterClass.ResourceType.RAGE: return "Wut"
	return ""

# Equipment-Stats auf Charakter anwenden
func _apply_equipment_stats():
	armor = 0
	haste = 0.0
	var bonus_str = 0
	var bonus_agi = 0
	var bonus_int = 0
	var bonus_sta = 0

	for slot in equipment.keys():
		var item = equipment[slot]
		if item != null:
			armor += item.armor
			haste += item.haste
			bonus_str += item.strength
			bonus_agi += item.agility
			bonus_int += item.intelligence
			bonus_sta += item.stamina

	strength += bonus_str
	agility += bonus_agi
	intelligence += bonus_int
	stamina += bonus_sta

	# HP und Ressource neu berechnen mit Equipment-Boni
	max_hp = stamina * CharacterClass.HP_PER_STAMINA
	_calculate_resource()

# Equipment anlegen
func equip_item(item: Equipment) -> Equipment:
	var old_item = equipment[item.slot]
	equipment[item.slot] = item
	_calculate_stats()
	# HP proportional anpassen
	if max_hp > 0:
		current_hp = mini(current_hp, max_hp)
	hud.update_health(current_hp, max_hp)
	character_panel.update_stats(self)
	print("Ausgerüstet: ", item.item_name, " in Slot ", Equipment.get_slot_name(item.slot))
	return old_item

# Equipment ablegen
func unequip_slot(slot: Equipment.Slot) -> Equipment:
	var old_item = equipment[slot]
	if old_item == null:
		return null
	equipment[slot] = null
	_calculate_stats()
	current_hp = mini(current_hp, max_hp)
	hud.update_health(current_hp, max_hp)
	character_panel.update_stats(self)
	print("Abgelegt: ", old_item.item_name)
	return old_item

# Ausgerüstete Waffe holen
func get_equipped_weapon() -> Equipment:
	return equipment[Equipment.Slot.WEAPON]

# Main-Stat für Schadensberechnung holen
func get_main_stat() -> int:
	if character_class == null:
		return 10
	match character_class.main_stat:
		CharacterClass.MainStat.STRENGTH:
			return strength
		CharacterClass.MainStat.AGILITY:
			return agility
		CharacterClass.MainStat.INTELLIGENCE:
			return intelligence
	return 10

# XP erhalten und Level-Up prüfen
func gain_xp(amount: int):
	current_xp += amount
	print("+" , amount, " XP (", current_xp, "/", xp_to_next_level, ")")

	while current_xp >= xp_to_next_level:
		_level_up()

	hud.update_level(level, current_xp, xp_to_next_level)

# Level-Up durchführen
func _level_up():
	current_xp -= xp_to_next_level
	level += 1
	xp_to_next_level = _calculate_xp_for_level(level + 1)

	# Stats neu berechnen
	_calculate_stats()

	# HP und Ressource vollständig auffüllen bei Level-Up
	current_hp = max_hp
	current_resource = max_resource

	hud.update_health(current_hp, max_hp)
	hud.update_resource(current_resource, max_resource, get_resource_name())
	# Character Panel aktualisieren falls offen
	character_panel.update_stats(self)
	print("LEVEL UP! Jetzt Level ", level, " - HP und Ressource voll aufgefüllt!")

# XP-Kurve: Jedes Level braucht mehr XP
func _calculate_xp_for_level(target_level: int) -> int:
	return 100 * target_level  # Level 2: 100, Level 3: 200, etc.

# Handler für HUD-Slot-Klicks
func _on_slot_clicked(slot_index: int):
	_use_action_slot(slot_index)

# Slot aus Aktionsleiste entfernen (rausgezogen) - Item/Skill bleibt verfügbar
func _on_slot_drag_removed(slot_index: int):
	action_bar_items[slot_index] = null
	hud.clear_slot_icon(slot_index)
	hud.set_slot_stack_count(slot_index, 0)
	print("Slot " + str(slot_index + 1) + " geleert")

# Zwei Slots tauschen
func _on_slot_drag_swapped(from_slot: int, to_slot: int):
	var temp = action_bar_items[from_slot]
	action_bar_items[from_slot] = action_bar_items[to_slot]
	action_bar_items[to_slot] = temp
	_refresh_action_slot(from_slot)
	_refresh_action_slot(to_slot)

# Skill per ID auf Slot legen
func assign_skill_to_action_bar(slot_index: int, skill_id: String):
	action_bar_items[slot_index] = skill_id
	_refresh_action_slot(slot_index)
	print(skill_id + " auf Slot " + str(slot_index + 1) + " gelegt")

# Skill-Info anhand ID holen
func get_skill_info(skill_id: String) -> Dictionary:
	for skill in available_skills:
		if skill["id"] == skill_id:
			return skill
	return {}

# Cooldown für einen Slot ermitteln
func _get_slot_cooldown(slot_index: int) -> float:
	var entry = action_bar_items[slot_index]
	if entry is String:
		match entry:
			"autoattack":
				return global_cooldown
			"wand":
				return global_cooldown
			"heavy_strike":
				return heavy_strike_cooldown
			"frostbolt":
				return frostbolt_cooldown
	elif entry is Consumable:
		return potion_cooldown
	return 0.0

func _refresh_action_slot(slot_index: int):
	var entry = action_bar_items[slot_index]
	if entry is String:
		# Skill
		var info = get_skill_info(entry)
		if info.size() > 0:
			hud.set_slot_icon(slot_index, info["icon"])
		else:
			hud.clear_slot_icon(slot_index)
		hud.set_slot_stack_count(slot_index, 0)
	elif entry is Consumable and entry.icon:
		hud.set_slot_icon_texture(slot_index, entry.icon)
		hud.set_slot_stack_count(slot_index, entry.stack_size)
	else:
		hud.clear_slot_icon(slot_index)
		hud.set_slot_stack_count(slot_index, 0)

func _use_action_slot(slot_index: int):
	var entry = action_bar_items[slot_index]
	if entry is String:
		# Skill ausführen
		match entry:
			"autoattack":
				if target != null and global_cooldown <= 0:
					wand_active = false  # Zauberstab deaktivieren
					start_autoattack()
					perform_autoattack()
			"wand":
				if target != null and global_cooldown <= 0:
					autoattack_active = false  # Autoattack deaktivieren
					start_wand()
					perform_wand_attack()
			"heavy_strike":
				use_heavy_strike()
			"frostbolt":
				use_frostbolt()
	elif entry is Consumable:
		if use_consumable(entry):
			if entry.stack_size <= 0:
				inventory.remove_item(entry)
		_update_action_bar_stacks()

# Schaden am Spieler abziehen und HP-Leiste aktualisieren
func take_damage(amount):
	current_hp = clamp(current_hp - amount, 0, max_hp)
	hud.update_health(current_hp, max_hp)
	if is_casting:
		_cancel_cast()
	if current_hp <= 0:
		die()

# Schaden mit Rüstung und Level-Differenz berechnen
func calculate_incoming_damage(raw_damage: int, attacker_level: int, is_melee: bool) -> int:
	var damage = float(raw_damage)

	# Rüstung reduziert nur Nahkampfschaden
	if is_melee and armor > 0:
		var armor_reduction = float(armor) / (float(armor) + 50.0)
		damage = damage * (1.0 - armor_reduction)

	# Level-Differenz Modifikator (Gegner höheres Level = mehr Schaden)
	var level_diff = attacker_level - level
	var level_mod = clamp(level_diff * LEVEL_DIFF_DAMAGE_MOD, -MAX_LEVEL_DIFF_MOD, MAX_LEVEL_DIFF_MOD)
	damage = damage * (1.0 + level_mod)

	return maxi(1, int(damage))

# Schaden mit vollem Schadenssystem nehmen
func take_damage_from(raw_damage: int, attacker_level: int, is_melee: bool = true):
	var final_damage = calculate_incoming_damage(raw_damage, attacker_level, is_melee)
	print("Spieler nimmt Schaden: ", raw_damage, " -> ", final_damage, " (nach Rüstung/Level)")
	take_damage(final_damage)

# HP heilen und HP-Leiste aktualisieren
func heal(amount):
	current_hp = clamp(current_hp + amount, 0, max_hp)
	hud.update_health(current_hp, max_hp)

# Ressource wiederherstellen (Mana/Energie/Wut)
func restore_mana(amount):
	current_resource = clamp(current_resource + amount, 0, max_resource)
	hud.update_resource(current_resource, max_resource, get_resource_name())

# Ressource verbrauchen
func spend_resource(amount) -> bool:
	if current_resource < amount:
		print("Nicht genug " + get_resource_name() + "!")
		return false
	current_resource = clamp(current_resource - amount, 0, max_resource)
	hud.update_resource(current_resource, max_resource, get_resource_name())
	return true

# Consumable benutzen (Trank etc.)
func use_consumable(consumable: Consumable) -> bool:
	if potion_cooldown > 0:
		print("Trank noch im Cooldown!")
		return false
	if consumable.use(self):
		potion_cooldown = consumable.cooldown
		if consumable.stack_size <= 0:
			return true  # Verbraucht
	return false

# Consumable auf Aktionsleiste legen
func assign_to_action_bar(slot_index: int, consumable: Consumable):
	if slot_index < 2 or slot_index > 8:
		return  # Slot 0+1 sind reserviert für Skills
	action_bar_items[slot_index] = consumable
	if consumable and consumable.icon:
		hud.set_slot_icon_texture(slot_index, consumable.icon)
		hud.set_slot_stack_count(slot_index, consumable.stack_size)
	else:
		hud.clear_slot_icon(slot_index)
		hud.set_slot_stack_count(slot_index, 0)

# Aktionsleiste Stack-Counts aktualisieren
func _update_action_bar_stacks():
	for i in range(2, 9):
		var item = action_bar_items[i]
		if item is Consumable:
			if item.stack_size <= 0:
				action_bar_items[i] = null
				hud.clear_slot_icon(i)
				hud.set_slot_stack_count(i, 0)
			else:
				hud.set_slot_stack_count(i, item.stack_size)

# Loot empfangen und Fenster anzeigen
func receive_loot(loot: Dictionary, world_pos: Vector3):
	loot_window.show_loot(loot, world_pos)

func die():
	print("Spieler gestorben!")
	_play_attack_anim("die")

# Schaden basierend auf ausgerüsteter Waffe + Main-Stat Skalierung
func get_attack_damage() -> int:
	var weapon = get_equipped_weapon()
	var base_damage: int
	if weapon == null:
		# Unbewaffneter Schaden klassenabhängig
		if character_class:
			base_damage = randi_range(character_class.unarmed_min_damage, character_class.unarmed_max_damage)
		else:
			base_damage = 1
	else:
		base_damage = randi_range(weapon.min_damage, weapon.max_damage)

	# Schaden skaliert mit Main-Stat
	var stat_bonus = int(get_main_stat() * CharacterClass.DAMAGE_PER_MAIN_STAT)
	return base_damage + stat_bonus

# Aktuellen GCD berechnen (mit Haste-Modifikator)
func get_current_gcd() -> float:
	var weapon = get_equipped_weapon()
	var base_speed: float
	if weapon == null:
		# Unbewaffnete Angriffsgeschwindigkeit klassenabhängig
		if character_class:
			base_speed = character_class.unarmed_attack_speed
		else:
			base_speed = BASE_GCD
	else:
		base_speed = weapon.attack_speed

	# Haste reduziert den GCD: GCD = Basis / (1 + Haste)
	# Bei 0.5 Haste (50%): 1.5s / 1.5 = 1.0s
	return base_speed / (1.0 + haste)

# DPS berechnen (für Anzeige)
func get_dps() -> float:
	var weapon = get_equipped_weapon()
	var avg_damage: float
	if weapon == null:
		# Unbewaffneter Durchschnittsschaden klassenabhängig
		if character_class:
			avg_damage = (character_class.unarmed_min_damage + character_class.unarmed_max_damage) / 2.0
		else:
			avg_damage = 1.0
	else:
		avg_damage = (weapon.min_damage + weapon.max_damage) / 2.0

	var stat_bonus = get_main_stat() * CharacterClass.DAMAGE_PER_MAIN_STAT
	var total_damage = avg_damage + stat_bonus
	var gcd = get_current_gcd()

	# DPS = Schaden / GCD
	return total_damage / gcd

# Reichweite basierend auf ausgerüsteter Waffe (Nahkampf)
func get_attack_range() -> float:
	var weapon = get_equipped_weapon()
	if weapon == null:
		return 3.0
	return weapon.weapon_range

# Ziel markieren — start_attack=true startet sofort die Autoattack
func set_target(new_target, start_attack: bool = false):
	if target != null and is_instance_valid(target):
		target.hide_health()
	target = new_target
	target.show_health()
	print("Ziel markiert: ", target.name)
	if start_attack:
		_start_default_attack()

# Ziel komplett aufheben und Autoattack stoppen
func clear_target():
	if target != null and is_instance_valid(target):
		target.hide_health()
	target = null
	autoattack_active = false
	wand_active = false
	print("Ziel aufgehoben, Angriff gestoppt")

# Standard-Angriff starten (Rechtsklick): Magier=Zauberstab, Rest=Autoattack
func _start_default_attack():
	if character_class and character_class.resource_type == CharacterClass.ResourceType.MANA:
		start_wand()
		if global_cooldown <= 0:
			perform_wand_attack()
	else:
		start_autoattack()
		if global_cooldown <= 0:
			perform_autoattack()

# Autoattack aktivieren
func start_autoattack():
	autoattack_active = true
	print("Autoattack aktiviert")

# Autoattack deaktivieren
func stop_autoattack():
	autoattack_active = false
	print("Autoattack deaktiviert")

# Zauberstab aktivieren (deaktiviert Autoattack)
func start_wand():
	wand_active = true
	autoattack_active = false
	print("Zauberstab aktiviert")

# Zauberstab deaktivieren
func stop_wand():
	wand_active = false
	print("Zauberstab deaktiviert")

# Zauberstab-Angriff ausführen (Fernkampf, magisch)
func perform_wand_attack():
	if target == null or not is_instance_valid(target):
		target = null
		wand_active = false
		return

	var distance = global_position.distance_to(target.global_position)
	if distance <= WAND_RANGE:
		var dmg = get_attack_damage()
		if target.has_method("take_damage_from"):
			target.take_damage_from(dmg, level, false)  # Magisch, ignoriert Rüstung
		else:
			target.take_damage(dmg)
		print("Zauberstab: ", dmg, " magischer Schaden")
		_play_attack_anim("autoattack")
		trigger_global_cooldown()

# Führt einen Autoattack aus (wird vom GCD-System aufgerufen)
func perform_autoattack():
	if target == null or not is_instance_valid(target):
		target = null
		autoattack_active = false
		return

	var distance = global_position.distance_to(target.global_position)
	if distance <= get_attack_range():
		var dmg = get_attack_damage()
		if target.has_method("take_damage_from"):
			target.take_damage_from(dmg, level, true)  # Nahkampf
		else:
			target.take_damage(dmg)
		print("Autoattack: ", dmg, " Schaden (GCD: %.2fs, DPS: %.1f)" % [get_current_gcd(), get_dps()])
		_play_attack_anim("autoattack")
		trigger_global_cooldown()

# Global Cooldown auslösen (basierend auf Waffe + Haste)
func trigger_global_cooldown():
	global_cooldown = get_current_gcd()

# Heavy Strike: Starker Angriff mit Cooldown
func use_heavy_strike():
	if target == null or not is_instance_valid(target):
		print("Kein Ziel für Heavy Strike!")
		return

	# Nur Skill-eigener Cooldown Check (kein GCD-Check!)
	if heavy_strike_cooldown > 0:
		print("Heavy Strike noch im Cooldown: ", "%.1f" % heavy_strike_cooldown, "s")
		return

	var distance = global_position.distance_to(target.global_position)
	if distance > HEAVY_STRIKE_RANGE:
		print("Ziel zu weit entfernt für Heavy Strike!")
		return

	var base_damage = randi_range(HEAVY_STRIKE_DAMAGE_MIN, HEAVY_STRIKE_DAMAGE_MAX)
	var stat_bonus = int(get_main_stat() * CharacterClass.DAMAGE_PER_MAIN_STAT)
	var damage = base_damage + stat_bonus
	# Neues Schadenssystem mit Rüstung und Level-Differenz
	if target.has_method("take_damage_from"):
		target.take_damage_from(damage, level, true)  # true = Nahkampf
	else:
		target.take_damage(damage)
	heavy_strike_cooldown = HEAVY_STRIKE_COOLDOWN
	_play_attack_anim("heavy_strike")
	trigger_global_cooldown()  # GCD zurücksetzen damit Autoattack nicht sofort feuert
	start_autoattack()  # Autoattack nach Skill automatisch aktivieren
	print("Heavy Strike! ", damage, " Rohschaden")

# Frostblitz: Cast starten
func use_frostbolt():
	if is_casting:
		return  # Bereits am Casten
	if target == null or not is_instance_valid(target):
		print("Kein Ziel für Frostblitz!")
		return
	if frostbolt_cooldown > 0:
		print("Frostblitz noch im Cooldown: ", "%.1f" % frostbolt_cooldown, "s")
		return
	if current_resource < FROSTBOLT_MANA_COST:
		print("Nicht genug Mana für Frostblitz! (", current_resource, "/", FROSTBOLT_MANA_COST, ")")
		return
	var distance = global_position.distance_to(target.global_position)
	if distance > FROSTBOLT_RANGE:
		print("Ziel zu weit entfernt für Frostblitz!")
		return

	# Cast starten
	_start_cast("frostbolt", FROSTBOLT_CAST_TIME)
	print("Frostblitz wird gewirkt... (", FROSTBOLT_CAST_TIME, "s)")

# Frostblitz: Schaden anwenden nach erfolgreichem Cast
func _finish_frostbolt():
	if target == null or not is_instance_valid(target):
		print("Ziel verloren!")
		return
	var distance = global_position.distance_to(target.global_position)
	if distance > FROSTBOLT_RANGE:
		print("Ziel zu weit entfernt!")
		return

	# Mana abziehen
	spend_resource(FROSTBOLT_MANA_COST)

	var base_damage = randi_range(FROSTBOLT_DAMAGE_MIN, FROSTBOLT_DAMAGE_MAX)
	var stat_bonus = int(intelligence * CharacterClass.DAMAGE_PER_MAIN_STAT)
	var damage = base_damage + stat_bonus
	if target.has_method("take_damage_from"):
		target.take_damage_from(damage, level, false)
	else:
		target.take_damage(damage)
	frostbolt_cooldown = FROSTBOLT_COOLDOWN
	trigger_global_cooldown()
	start_wand()  # Zauberstab nach Cast weiter aktiv
	print("Frostblitz! ", damage, " magischer Schaden (", FROSTBOLT_MANA_COST, " Mana)")

# Cast-System
func _start_cast(spell_id: String, cast_time: float):
	is_casting = true
	cast_spell_id = spell_id
	cast_time_total = cast_time
	cast_time_remaining = cast_time
	autoattack_active = false  # Autoattack pausieren während Cast
	hud.show_castbar(spell_id, cast_time)

func _cancel_cast():
	if not is_casting:
		return
	is_casting = false
	cast_spell_id = ""
	cast_time_remaining = 0.0
	hud.hide_castbar()
	print("Zauber unterbrochen!")

func _finish_cast():
	var spell = cast_spell_id
	is_casting = false
	cast_spell_id = ""
	cast_time_remaining = 0.0
	hud.hide_castbar()
	# Fertigen Zauber ausführen
	match spell:
		"frostbolt":
			_finish_frostbolt()

# Raycast von der Kamera auf Mausposition — trifft Gegner mit take_damage()
func _try_select_target(start_attack: bool = false):
	var space_state = get_world_3d().direct_space_state
	var viewport = get_viewport()
	var mouse_pos = viewport.get_mouse_position()
	var ray_origin = camera.project_ray_origin(mouse_pos)
	var ray_end = ray_origin + camera.project_ray_normal(mouse_pos) * 100.0
	var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_end)
	query.exclude = [self]
	var result = space_state.intersect_ray(query)
	if result and result.collider.has_method("take_damage"):
		set_target(result.collider, start_attack)
	elif not start_attack:
		# Nur bei Linksklick ins Leere: Ziel deselektieren und Autoattack stoppen
		# Rechtsklick wird für Kameradrehung verwendet
		clear_target()

func _physics_process(delta):
	# Cast-System
	if is_casting:
		cast_time_remaining -= delta
		hud.update_castbar(cast_time_total - cast_time_remaining, cast_time_total)
		if cast_time_remaining <= 0:
			_finish_cast()

	# Global Cooldown herunterzählen (gilt für alle Aktionen)
	if global_cooldown > 0:
		global_cooldown -= delta

	# Wenn GCD bereit und nicht am Casten
	if global_cooldown <= 0 and not is_casting:
		if wand_active:
			perform_wand_attack()
		elif autoattack_active:
			perform_autoattack()

	# Skill-Cooldowns herunterzählen
	if heavy_strike_cooldown > 0:
		heavy_strike_cooldown -= delta
	if frostbolt_cooldown > 0:
		frostbolt_cooldown -= delta
	if potion_cooldown > 0:
		potion_cooldown -= delta

	# HUD Cooldowns aktualisieren - generisch pro Slot
	for i in range(9):
		var cd = _get_slot_cooldown(i)
		hud.set_slot_cooldown(i, cd)

	# Schwerkraft
	if not is_on_floor():
		velocity.y -= GRAVITY * delta

	# Springen
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY
		if is_casting:
			_cancel_cast()

	# Linksklick: nur markieren
	if Input.is_action_just_pressed("select_target"):
		_try_select_target(false)

	# Rechtsklick: markieren + angreifen
	if Input.is_action_just_pressed("ui_right_mouse"):
		_try_select_target(true)

	# Aktionsleiste 1-9 — alle generisch über _use_action_slot
	for i in range(9):
		var action_name = "action_" + str(i + 1)
		if Input.is_action_just_pressed(action_name):
			hud.set_active_slot(i)
			_use_action_slot(i)

	# TEST: T drücken = 10 Schaden
	if Input.is_action_just_pressed("test_damage"):
		take_damage(10)

	# C drücken = Charakter-Panel öffnen/schließen
	if Input.is_action_just_pressed("toggle_character"):
		character_panel.update_stats(self)
		character_panel.toggle()

	# I drücken = Inventar öffnen/schließen
	if Input.is_action_just_pressed("toggle_inventory"):
		inventory_panel.toggle()

	# P drücken = Fähigkeiten-Panel öffnen/schließen
	if Input.is_action_just_pressed("toggle_skills"):
		skill_panel.toggle()

	# Eingabe
	var input_dir = Vector2.ZERO
	if Input.is_action_pressed("move_forward"):
		input_dir.y -= 1
	if Input.is_action_pressed("move_back"):
		input_dir.y += 1
	if Input.is_action_pressed("move_left"):
		input_dir.x -= 1
	if Input.is_action_pressed("move_right"):
		input_dir.x += 1

	# Bewegung unterbricht Cast
	if is_casting and input_dir.length() > 0:
		_cancel_cast()

	# Bewegung relativ zur Kamera
	var world_yaw = rotation.y + camera_pivot.rotation.y
	var forward = Vector3(-sin(world_yaw), 0, -cos(world_yaw)).normalized()
	var right = Vector3(cos(world_yaw), 0, -sin(world_yaw)).normalized()
	var direction = (forward * -input_dir.y + right * input_dir.x)

	velocity.x = direction.x * SPEED
	velocity.z = direction.z * SPEED

	# Animation aktualisieren
	_update_animation(input_dir)

	# RMB gehalten: Spieler schaut in Kamerarichtung
	if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT):
		rotation.y = world_yaw
		camera_pivot.rotation.y = 0

	move_and_slide()
