class_name BaseUnit
extends CharacterBody3D

const UnitHelper = preload("res://src/entities/units/unit_helper.gd")

## 플레이어 유닛 기본 클래스 (RTS 식 이동, 몬스터 자동 감지 및 공격 수행)

@export var unit_data: UnitData

var is_selected: bool = false

# 전투 관련 상태
var _target_monster: Node3D = null
var _attack_timer: float = 0.0

# 자동 탐색 범위 (맵이 소형화되었으므로 15.0m로 축소)
@export var detection_range: float = 15.0
var _auto_target_cooldown: float = 0.0


# 노드 캐싱
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
@onready var _selection_ring: MeshInstance3D = $SelectionRing
@onready var _visual_mesh: MeshInstance3D = $VisualMesh

# 홀드 상태 변수
var _is_holding: bool = false

# 3D 모델 및 애니메이션 연동 변수 (정적 타이핑 캐싱)
var _model_instance: Node3D = null
var _model_anim_player: AnimationPlayer = null
var _last_anim_state: String = ""
var _is_currently_attacking: bool = false
var _attack_lock_timer: float = 0.0 # 공격 모션 완주 및 물리 이동 차단을 위한 타이머

func _ready() -> void:
	# 유닛 그룹에 등록
	add_to_group("units")
	
	# 네비게이션 파라미터 조율
	_nav_agent.path_desired_distance = 0.5
	_nav_agent.target_desired_distance = 0.5
	
	# 선택 상태에 맞춤 설정
	deselect()
	
	# 유닛 능력치에 따른 외형(색상) 적용
	if unit_data:
		_apply_unit_style()
		
	# 자식 노드 중 3D 모델(striker, guardian 등) 노드 자동 탐색 및 초기화
	var model_names: Array[String] = ["striker", "guardian"]
	var detected_model_node: Node3D = null
	var detected_model_name: String = ""
	
	for m_name: String in model_names:
		var node: Node3D = get_node_or_null(m_name) as Node3D
		if node:
			detected_model_node = node
			detected_model_name = m_name
			break
			
	if detected_model_node:
		_model_instance = detected_model_node
		
		if detected_model_name == "striker":
			UnitHelper.apply_skin(_model_instance, "res://assets/models/units/striker_0.png")
			UnitHelper.attach_weapon(_model_instance, unit_data)
		elif detected_model_name == "guardian":
			UnitHelper.apply_skin(_model_instance, "res://assets/models/units/guardian/guardian_0.png")
			UnitHelper.attach_weapon(_model_instance, unit_data)
			
		_model_anim_player = UnitHelper.find_animation_player(_model_instance)
		if _model_anim_player:
			print("BaseUnit: Linked pre-configured model & AnimationPlayer: ", detected_model_name)
			
			# 모델명_animations.tres 라이브러리를 동적으로 로드하여 등록
			var anim_lib_path: String = "res://assets/models/units/" + detected_model_name + "/" + detected_model_name + "_animations.tres"
			if ResourceLoader.exists(anim_lib_path):
				var lib: AnimationLibrary = load(anim_lib_path) as AnimationLibrary
				if lib:
					_model_anim_player.add_animation_library(detected_model_name, lib)
					print("BaseUnit: Loaded and registered ", detected_model_name, " animations library successfully.")
					UnitHelper.clean_root_animation_tracks(_model_anim_player)



func _physics_process(delta: float) -> void:
	if GameManager.is_game_over:
		velocity = Vector3.ZERO
		return
		
	# 공격 타이머 및 자동 타겟팅 쿨타임 차감
	if _attack_timer > 0.0:
		_attack_timer -= delta
	if _attack_lock_timer > 0.0:
		_attack_lock_timer -= delta
	if _auto_target_cooldown > 0.0:
		_auto_target_cooldown -= delta
		
	# 1. 공격 대상이 유효한지 검증 (사망했거나 삭제되었는지 체크)
	if _target_monster and (not is_instance_valid(_target_monster) or _target_monster.get("is_dead")):
		_target_monster = null
		
	# 홀드 상태인데 타겟이 공격 유지 한계선(chase_threshold)을 벗어나면 타겟팅 해제하여 추격 방지
	if _is_holding and _target_monster:
		var dist: float = global_position.distance_to(_target_monster.global_position)
		var attack_range: float = unit_data.attack_range
		var buffer: float = 0.5 if not unit_data.is_ranged else maxf(0.8, attack_range * 0.1)
		var chase_threshold: float = attack_range + buffer
		if dist > chase_threshold:
			_target_monster = null
		
	# 2. 적 자동 탐색 (타겟이 없고 수동 강제 이동 방지 쿨다운이 완료된 경우)
	if not _target_monster and _auto_target_cooldown <= 0.0:
		_find_nearest_target()
		
	# 3. 상태에 따른 AI 동작 분기
	if _target_monster:
		_process_attack_logic(delta)
	else:
		_process_normal_logic(delta)
		
	# 4. 3D 애니메이션 재생 업데이트
	_update_animations()

## 1. 공격 타겟이 존재할 때의 동작 로직 (진동 버벅임 방지 Hysteresis 적용 및 공격 중 잠금 추가)
func _process_attack_logic(_delta: float) -> void:
	if not _target_monster:
		_is_currently_attacking = false
		_attack_lock_timer = 0.0
		return
		
	# 공격 모션 재생 중(공격 잠금 시간 동안)에는 무조건 이동하지 않고 멈춰서 공격 모션 유지
	if _attack_lock_timer > 0.0:
		_is_currently_attacking = true
		velocity = Vector3.ZERO
		# 타겟 몬스터를 바라보도록 회전
		_look_at_target(_target_monster.global_position)
		return
		
	var distance_to_target: float = global_position.distance_to(_target_monster.global_position)
	
	# 완충 사거리 마진 (Hysteresis): 이동-정지-이동 반복으로 틱틱 끊기는 버벅임 방지
	var attack_range: float = unit_data.attack_range
	# 근접은 0.5m, 원거리는 사거리의 10%(최소 0.8m) 버퍼를 줌
	var buffer: float = 0.5 if not unit_data.is_ranged else maxf(0.8, attack_range * 0.1)
	var chase_threshold: float = attack_range + buffer
	
	# 공격 개시/유지 조건 판정
	var should_attack: bool = false
	if _is_currently_attacking:
		# 이미 때리고 있다면 마진 한계선(chase_threshold)을 넘어가기 전까지 계속 제자리 타격
		should_attack = (distance_to_target <= chase_threshold)
	else:
		# 추격 중이라면 원래의 사거리(attack_range) 이내로 들어와야 공격 모드 진입
		should_attack = (distance_to_target <= attack_range)
		
	if should_attack:
		_is_currently_attacking = true
		velocity = Vector3.ZERO
		# 타겟 몬스터를 바라보도록 회전
		_look_at_target(_target_monster.global_position)
		_try_attack()
	else:
		_is_currently_attacking = false
		# 사거리 밖인 경우 추격 이동
		_nav_agent.target_position = _target_monster.global_position
		_navigate_to_target(_delta)

## 2. 일반 대기 및 이동 상태의 로직
func _process_normal_logic(_delta: float) -> void:
	# 홀드 상태이거나 이동 명령이 끝난 경우 정지
	if _is_holding or _nav_agent.is_navigation_finished():
		velocity = Vector3.ZERO
	else:
		_navigate_to_target(_delta)

## 네비게이션 타겟 방향으로 캐릭터 이동
func _navigate_to_target(_delta: float) -> void:
	if _nav_agent.is_navigation_finished():
		velocity = Vector3.ZERO
		return
		
	var next_path_pos: Vector3 = _nav_agent.get_next_path_position()
	var current_pos: Vector3 = global_position
	var new_velocity: Vector3 = (next_path_pos - current_pos).normalized() * unit_data.move_speed
	
	# Y축 속도는 고정하고 평면 속도만 적용
	velocity = Vector3(new_velocity.x, 0.0, new_velocity.z)
	move_and_slide()
	
	# 이동 방향 바라보도록 회전
	if current_pos.distance_to(next_path_pos) > 0.2:
		_look_at_target(next_path_pos)

## 타겟을 부드럽게(또는 수평 기준) 바라보게 회전
func _look_at_target(target_pos: Vector3) -> void:
	var look_target: Vector3 = Vector3(target_pos.x, global_position.y, target_pos.z)
	if global_position.distance_to(look_target) > 0.1:
		look_at(look_target, Vector3.UP)

## 범위 내 가장 가까운 살아있는 몬스터 탐색
func _find_nearest_target() -> void:
	var monsters: Array[Node] = get_tree().get_nodes_in_group("monsters")
	var nearest_monster: Node3D = null
	# 홀드 상태 시에는 자신의 무기 사거리 내만 탐색, 일반 상태 시에는 자동 탐색 범위(15m) 적용
	var min_distance: float = unit_data.attack_range if _is_holding else detection_range
	
	for monster: Node in monsters:
		if is_instance_valid(monster) and monster is Node3D:
			var m_3d: Node3D = monster as Node3D
			# 몬스터의 사망 여부 검사
			if m_3d.get("is_dead") == true:
				continue
				
			var dist: float = global_position.distance_to(m_3d.global_position)
			if dist < min_distance:
				min_distance = dist
				nearest_monster = m_3d
				
	if nearest_monster:
		_target_monster = nearest_monster

## 타겟에게 피격 가함
func _try_attack() -> void:
	if _attack_timer > 0.0 or not _target_monster:
		return
		
	# 쿨타임 타이머 작동
	_attack_timer = unit_data.cooldown
	
	# 공격 비주얼 연출
	_show_attack_visual()
	
	# 공격 애니메이션 재생 길이에 맞춘 이동 제어 락 타이머 설정
	var anim_length: float = 0.5 # 기본값
	var play_speed: float = 1.5
	
	if _model_instance and _model_anim_player:
		var model_name: String = _model_instance.name.to_lower()
		var library_prefix: String = model_name + "/"
		var attack_anim: String = library_prefix + "attack"
		
		if _model_anim_player.has_animation(attack_anim):
			anim_length = _model_anim_player.get_animation(attack_anim).length
		elif _model_anim_player.has_animation("Armature|Armature|Basic_Jump|baselayer"):
			anim_length = _model_anim_player.get_animation("Armature|Armature|Basic_Jump|baselayer").length
			
		if unit_data.cooldown < 0.6:
			play_speed = clampf(0.6 / unit_data.cooldown, 1.0, 3.0)
			
	# 공격 락 시간 계산 (애니메이션 실재생 속도 반영)
	_attack_lock_timer = anim_length / play_speed
	# 락 타이머가 공격 쿨다운보다 길어지는 것은 방지
	if _attack_lock_timer > unit_data.cooldown:
		_attack_lock_timer = unit_data.cooldown
		
	# 데미지 적용 및 발사체 발사 처리 분기
	if unit_data.is_ranged:
		# 원거리 유닛: 즉발로 유도 발사체(Projectile) 생성하여 발사
		var projectile_script = preload("res://src/entities/units/projectile.gd")
		var proj: Node3D = projectile_script.new() as Node3D
		proj.set("damage", unit_data.damage)
		proj.set("target", _target_monster)
		proj.set("color", unit_data.unit_color)
		
		# 유닛 중심보다 높은 곳(어깨 높이)에서 발사 처리
		proj.global_position = global_position + Vector3(0.0, 0.7, 0.0)
		get_tree().current_scene.add_child(proj)
	else:
		# 근접 유닛: 공격 애니메이션 중간 즈음(락아웃 시간의 50% 지점)에 데미지 지연 적용
		var target_ref: Node3D = _target_monster
		var damage_amount: int = unit_data.damage
		var damage_delay: float = _attack_lock_timer * 0.5
		
		if damage_delay > 0.0:
			get_tree().create_timer(damage_delay).timeout.connect(
				func() -> void:
					if is_instance_valid(target_ref) and not target_ref.get("is_dead"):
						if target_ref.has_method("take_damage"):
							target_ref.call("take_damage", damage_amount)
			)
		else:
			if target_ref.has_method("take_damage"):
				target_ref.call("take_damage", damage_amount)

## 공격 관련 비주얼 이펙트 연출
func _show_attack_visual() -> void:
	# 원거리 타격 시 빔 또는 스파크 이펙트 임시 렌더
	if unit_data.is_ranged and _target_monster:
		print("Unit ", name, " fired a projectile/spell at ", _target_monster.name)
	else:
		print("Unit ", name, " strikes ", _target_monster.name)

## 유닛을 제자리에 고정하고 사거리 내의 적만 공격하도록 설정 (Hold)
func hold_position() -> void:
	_is_holding = true
	_target_monster = null
	_nav_agent.target_position = global_position
	velocity = Vector3.ZERO

## 외부에서 호출하는 수동 이동 명령
func move_to(target_pos: Vector3) -> void:
	_is_holding = false # 수동 조작 시 홀드 해제
	_target_monster = null # 이동 명령 시 강제 공격 해제
	_attack_lock_timer = 0.0 # 공격 애니메이션 진행 중이더라도 강제 이동 명령 시 즉시 캔슬하고 이동 (스타크래프트 식 무빙 캔슬)
	_is_currently_attacking = false
	_auto_target_cooldown = 0.5 # 수동 강제 이동 시 0.5초 동안 자동 공격 타겟 지정을 비활성화하여 피신을 허용
	_nav_agent.target_position = target_pos

## 외부에서 호출하는 특정 몬스터 강제 공격 지정
func attack_target(monster: Node3D) -> void:
	_is_holding = false # 강제 공격 시 홀드 해제
	if is_instance_valid(monster) and not monster.get("is_dead"):
		_target_monster = monster
		_nav_agent.target_position = monster.global_position

## 유닛 데이터 스킨 색상 적용
func _apply_unit_style() -> void:
	# VisualMesh의 material에 접근하여 색상 덧칠
	if _visual_mesh and _visual_mesh.mesh:
		# Material override가 있는지 확인하고 동적 생성
		var mat: StandardMaterial3D = StandardMaterial3D.new()
		mat.albedo_color = unit_data.unit_color
		mat.roughness = 0.4
		_visual_mesh.material_override = mat

## 유닛 선택 시 하이라이팅
func select() -> void:
	is_selected = true
	if _selection_ring:
		_selection_ring.visible = true

## 유닛 선택 해제
func deselect() -> void:
	is_selected = false
	if _selection_ring:
		_selection_ring.visible = false

## 상태(대기, 이동, 공격)별 3D 애니메이션 트랙 제어 및 크로스페이드 블렌딩
func _update_animations() -> void:
	if not _model_anim_player:
		return
		
	var model_name: String = ""
	if _model_instance:
		model_name = _model_instance.name.to_lower()
		
	var library_prefix: String = model_name + "/"
	var has_custom_anims: bool = _model_anim_player.has_animation(library_prefix + "idle")
	
	var target_anim: String = ""
	var play_speed: float = 1.0
	var blend_time: float = 0.2 # 뚝뚝 끊기는 현상을 없애기 위한 0.2초 부드러운 크로스페이드
	
	if has_custom_anims:
		var attack_anim: String = library_prefix + "attack"
		var run_anim: String = library_prefix + "run"
		var idle_anim: String = library_prefix + "idle"
		
		# 1. 공격 락이 활성화되어 있는 동안에는 공격 애니메이션 강제 재생
		if _attack_lock_timer > 0.0:
			var base_speed: float = 1.5
			if unit_data.cooldown < 0.6:
				base_speed = 0.6 / unit_data.cooldown
			play_speed = clampf(base_speed, 1.0, 3.0)
			target_anim = attack_anim
		# 2. 이동 중 상태: 속도가 있을 때
		elif velocity.length() > 0.1:
			target_anim = run_anim
			play_speed = 1.2
		# 3. 대기 상태: 정지 상태 및 쿨다운 중 대기할 때
		else:
			target_anim = idle_anim
			play_speed = 1.0
			
		# 애니메이션 전환 및 크로스페이드 블렌딩 실행
		if target_anim != "" and _model_anim_player.current_animation != target_anim:
			_last_anim_state = target_anim.replace(library_prefix, "")
			_model_anim_player.play(target_anim, blend_time, play_speed)
	else:
		# Fallback: 기존 캡슐 Jump 애니메이션 처리
		var fallback_anim: String = "Armature|Armature|Basic_Jump|baselayer"
		if not _model_anim_player.has_animation(fallback_anim):
			return
			
		if _attack_lock_timer > 0.0:
			target_anim = fallback_anim
			play_speed = 2.5
		elif velocity.length() > 0.1:
			target_anim = fallback_anim
			play_speed = 1.5
		else:
			target_anim = ""
			_model_anim_player.stop()
			
		if target_anim != "" and _model_anim_player.current_animation != target_anim:
			_model_anim_player.play(target_anim, blend_time, play_speed)
