extends CharacterBody3D

const SKELETON_WARRIOR: PackedScene = preload("res://assets/models/characters/Skeleton_Warrior.glb")
const SKELETON_MAGE: PackedScene = preload("res://assets/models/characters/Skeleton_Mage.glb")

@export var stats: EnemyStats

var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
var spawn_scale: float = 1.0
var anim_player: AnimationPlayer = null
var current_anim: String = ""
var attack_lock_until: float = 0.0
var is_dying: bool = false

func _ready() -> void:
  add_to_group("enemies")
  if is_in_group("boss"):
    BossData.register(self, stats, spawn_scale)
    BossData.set_stat(self, "spawn_position", global_position)
    _swap_model(SKELETON_MAGE, 1.3)
  else:
    EnemyData.register(self, stats, spawn_scale)
    EnemyData.set_stat(self, "spawn_position", global_position)
  EventBus.entity_died.connect(_on_entity_died)
  EventBus.attack_executed.connect(_on_attack_executed)
  call_deferred("_check_variant")
  call_deferred("_init_anim")

func _init_anim() -> void:
  anim_player = get_node_or_null("Mesh/Model/AnimationPlayer")
  _play_anim("Idle")

func _check_variant() -> void:
  if is_in_group("boss"):
    return
  if is_in_group("red_enemies") or is_in_group("invasion"):
    _swap_model(SKELETON_WARRIOR, 1.0)
    anim_player = get_node_or_null("Mesh/Model/AnimationPlayer")
    _play_anim("Idle")

func _swap_model(new_scene: PackedScene, scale_factor: float = 1.0) -> void:
  var mesh: Node3D = get_node_or_null("Mesh")
  if not mesh:
    return
  var old: Node = mesh.get_node_or_null("Model")
  if old:
    old.queue_free()
  var new_model: Node3D = new_scene.instantiate()
  new_model.name = "Model"
  new_model.scale = Vector3(scale_factor, scale_factor, scale_factor)
  new_model.position = Vector3(0, -0.75, 0)
  new_model.rotation.y = PI
  mesh.add_child(new_model)

func _exit_tree() -> void:
  if is_in_group("boss"):
    BossData.deregister(self)
  else:
    EnemyData.deregister(self)

func _on_entity_died(entity: Node) -> void:
  if entity != self:
    return
  is_dying = true
  _play_anim("Death_A", false)
  get_tree().create_timer(1.0).timeout.connect(queue_free)

func _on_attack_executed(attacker: Node, _pos: Vector3, _dir: Vector3, _damage: float) -> void:
  if attacker != self:
    return
  _play_anim("1H_Melee_Attack_Chop", false)
  attack_lock_until = Time.get_ticks_msec() / 1000.0 + 0.5

func _process(_delta: float) -> void:
  if is_dying:
    return
  var now: float = Time.get_ticks_msec() / 1000.0
  if now < attack_lock_until:
    return
  if velocity.length() > 0.1:
    _play_anim("Running_A")
  else:
    _play_anim("Idle")

func _play_anim(anim_name: String, loop: bool = true) -> void:
  if not anim_player:
    return
  if current_anim == anim_name:
    return
  if not anim_player.has_animation(anim_name):
    return
  var anim: Animation = anim_player.get_animation(anim_name)
  if anim:
    anim.loop_mode = Animation.LOOP_LINEAR if loop else Animation.LOOP_NONE
  anim_player.play(anim_name)
  current_anim = anim_name

func _physics_process(delta: float) -> void:
  if not is_on_floor():
    velocity.y -= gravity * delta
  move_and_slide()
