class_name Hook
extends Node2D

## ================
## Export Field
## ================
@export var min_length := 140.0
@export var max_length := 200.0
@export var stretching_speed: float = 1400.0

@onready var line_2d: Line2D = %Line2D
@onready var ray: RayCast2D = %RayCast2D

const GRAPABLE_GROUP = &"GRAPABLE"

signal stretching_finished(reach_limit: bool, anchor_node: Node2D)

## ================
## Private Field
## ================
var _binded_hook_comp
var _is_stretching := false
var _stretching_dir := Vector2.ZERO
var _cached_cancel := false
var _current_length := 0.0
var _anchor: Node2D

var _dir_id: int = -1

# =================
# Life Cycle
# =================

func _ready() -> void:
	ray.enabled = true
	ray.target_position = Vector2.ZERO

## 初始化
func init(hook_comp: SpawnHookComponet, reset_to_target: bool) -> void:
	_binded_hook_comp = hook_comp
	if reset_to_target:
		global_position = hook_comp.owner.global_position

# =================
# Stretch Control
# =================

func start_stretching(direction: Vector2) -> void:
	_is_stretching = true
	_cached_cancel = false
	_stretching_dir = direction.normalized()
	_current_length = 0.0
	
	_dir_id = _get_direction_id(direction,8)

func end_stretching(force_end: bool = false) -> bool:
	if not force_end and _is_stretching:
		if _current_length < min_length:
			_cached_cancel = true
			return false

	_is_stretching = false
	_stretching_dir = Vector2.ZERO
	return true

func is_stretching() -> bool:
	return _is_stretching

# =================
# Update
# =================

func _physics_process(delta: float) -> void:
	if _is_stretching:
		_update_stretching(delta)

func _process(_delta: float) -> void:
	_update_line()

# =================
# Core Logic
# =================

func _update_stretching(delta: float) -> void:
	# 先嘗試推進
	var next_length := _current_length + stretching_speed * delta
	next_length = min(next_length, max_length)

	# 先用「下一幀長度」做 Ray
	ray.target_position = _stretching_dir * next_length
	ray.force_raycast_update()

	# ===== 命中檢測（最高優先）=====
	if ray.is_colliding():
		var collider := ray.get_collider()
		if collider is Node2D and collider.is_in_group(GRAPABLE_GROUP):
			var hit_pos := ray.get_collision_point()

			_current_length = global_position.distance_to(hit_pos)
			ray.target_position = _stretching_dir * _current_length
			_handle_hit(collider as Node2D, hit_pos)
			return

	# ===== 沒命中，才正式推進 =====
	_current_length = next_length

	# 取消邏輯
	if _cached_cancel and _current_length >= min_length:
		stretching_finished.emit(true, null)
		end_stretching(true)
		return

	# 超過最大距離
	if _current_length >= max_length:
		stretching_finished.emit(true, null)
		end_stretching(true)

func _handle_hit(target: Node2D, hit_pos: Vector2) -> void:
	_is_stretching = false
	_stretching_dir = Vector2.ZERO

	ray.target_position = to_local(hit_pos)

	var reach_max := is_equal_approx(_current_length, max_length)
	var anchor := _create_anchor_on_node(target, hit_pos)

	stretching_finished.emit(reach_max, anchor)

## 釋放鉤爪（清理 Anchor 與狀態）
func release_hook() -> void:
	# 1. 停止拉伸（保險）
	_is_stretching = false
	_stretching_dir = Vector2.ZERO
	_cached_cancel = false
	_current_length = 0.0

	# 2. 清掉 Anchor
	if _anchor and is_instance_valid(_anchor):
		# 先脫離父節點，避免殘留引用問題
		_anchor.get_parent().remove_child(_anchor)
		_anchor.queue_free()
		_anchor = null

	# 3. 重置 Ray 與 Line（視覺清乾淨）
	ray.target_position = Vector2.ZERO
	_update_line()

func _update_line() -> void:
	line_2d.set_point_position(0, Vector2.ZERO)
	line_2d.set_point_position(1, ray.target_position)

func _create_anchor_on_node(target: Node2D, hit_global_pos: Vector2) -> Node2D:
	if _anchor and is_instance_valid(_anchor):
		_anchor.queue_free()

	_anchor = Node2D.new()
	_anchor.name = "Anchor"
	target.add_child(_anchor)

	# 關鍵：固定命中點偏移
	_anchor.position = target.to_local(hit_global_pos)

	return _anchor

## 计算方向id，私有
func _get_direction_id(direction: Vector2, sector_count: int) -> int:
	if direction == Vector2.ZERO:
		return -1

	var angle = rad_to_deg(direction.angle())

	angle += 90.0

	var sector_size = 360.0 / sector_count
	angle += sector_size / 2.0

	angle = fposmod(angle, 360.0)

	return int(angle / sector_size)

## 获取方向id
func get_direction_id() -> int:
	return _dir_id
