extends CharacterBody3D

# ----------- CONSTANTS ------------
const LERP_VALUE: float = 0.15  # Smooth rotation factor
const ANIMATION_BLEND: float = 7.0  # Animation blending factor

# Movement Variables
@export var walk_speed: float = 200.0
@export var run_speed: float = 500.0
@export var jump_strength: float = 1500.0
@export var gravity: float = 5000.0
@export var _step_height: float = 1.0  # For stepping mechanics

# Dashing Variables
@export var dash_distance: float = 10.0  # Total dashing distance
@export var dash_speed: float = 40.0  # Dash speed
var is_dashing: bool = false  # Is the character dashing?
var dash_direction: Vector3 = Vector3.ZERO
var dash_time_left: float = 0.0

# UI References
@onready var inventory_ui = $InventoryUI  # Reference Inventory UI
@onready var pause_menu = $PauseMenu  # Reference the Pause Menu
@onready var player_mesh = $Mesh  # Reference player mesh
@onready var spring_arm_pivot = $SpringArmPivot  # Pivot for camera rotation
@onready var animator = $AnimationTree  # Animation tree

# Motion State
var local_velocity: Vector3 = Vector3.ZERO
var was_grounded: bool = false

# ----------- READY FUNCTION ------------
func _ready():
	if inventory_ui:
		inventory_ui.visible = false
		print("Inventory UI initialized successfully.")
	else:
		print("Error: Inventory UI is missing!")

	if pause_menu:
		pause_menu.visible = false
		print("Pause Menu initialized successfully.")
	else:
		print("Error: PauseMenu is missing!")

# ----------- INPUT HANDLING ------------
func _input(event):
	if event.is_action_pressed("pause_menu"):
		if pause_menu:
			pause_menu.toggle_pause_menu()

	if event.is_action_pressed("dash"):
		if not is_dashing:
			_start_dash()

	if event.is_action_pressed("open_inventory"):  # Debug for Inventory toggling
		toggle_inventory()

# ----------- INVENTORY MANAGEMENT ------------
func toggle_inventory():
	if inventory_ui:
		inventory_ui.toggle_inventory()
	else:
		print("Error: Inventory UI is not valid.")

func add_item(item_name: String, item_icon: Texture = null) -> bool:
	if inventory_ui.add_item(item_name, item_icon):
		print("Picked up: %s" % item_name)
		return true
	else:
		print("Failed to add %s to inventory." % item_name)
		return false

# ----------- PHYSICS AND MOVEMENT PROCESSING ------------
func _physics_process(delta: float):
	if is_dashing:
		_handle_dash(delta)
		return

	# Input-based movement direction
	var move_direction = Vector3.ZERO
	move_direction.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
	move_direction.z = Input.get_action_strength("move_forwards") - Input.get_action_strength("move_backwards")

	# Rotate movement direction to align with the camera
	move_direction = move_direction.rotated(Vector3.UP, spring_arm_pivot.rotation.y)

	# Determine speed (walk/run toggle)
	var move_speed: float = walk_speed
	if Input.is_action_pressed("run"):
		move_speed = run_speed

	# Update velocity
	local_velocity.x = move_direction.x * move_speed
	local_velocity.z = move_direction.z * move_speed
	local_velocity.y -= gravity * delta  # Gravity application

	# Jumping logic
	if is_on_floor() and Input.is_action_just_pressed("jump"):
		local_velocity.y = jump_strength

	# Smoothly rotate character mesh
	if move_direction != Vector3.ZERO:
		player_mesh.rotation.y = lerp_angle(
			player_mesh.rotation.y,
			atan2(move_direction.x, -move_direction.z),
			LERP_VALUE
		)

	# Move and terrain processing
	local_move()
	velocity = global_transform.basis * local_velocity
	move_and_slide()

	# Reset velocity for the next frame
	local_velocity = transform.basis.inverse() * velocity

	# Handle animations here
	animate(delta)

# ----------- ANIMATION PROCESSING ------------
func animate(delta: float):
	if is_on_floor():
		animator.set("parameters/ground_air_transition/transition_request", "grounded")

		# Movement animations (idle, walk, run)
		if local_velocity.length() > 0:  # Determine movement speed
			animator.set("parameters/iwr_blend/blend_amount", lerp(
				animator.get("parameters/iwr_blend/blend_amount"),
				1.0 if Input.is_action_pressed("run") else 0.0,  # Running or walking
				delta * ANIMATION_BLEND
			))
		else:
			animator.set("parameters/iwr_blend/blend_amount", lerp(
				animator.get("parameters/iwr_blend/blend_amount"),
				-1.0,  # Idle animation
				delta * ANIMATION_BLEND
			))
	else:
		# Airborne (jumping/falling)
		animator.set("parameters/ground_air_transition/transition_request", "air")

# ----------- DASH MECHANICS ------------
func _start_dash():
	is_dashing = true
	dash_time_left = dash_distance / dash_speed
	dash_direction = -player_mesh.basis.z.normalized()  # Forward direction
	print("Dash started: %s" % dash_direction)

func _handle_dash(delta: float):
	velocity += dash_direction * dash_speed * delta
	move_and_slide()

	dash_time_left -= delta
	if dash_time_left <= 0.0:
		_stop_dash()

func _stop_dash():
	is_dashing = false
	dash_direction = Vector3.ZERO
	print("Dash stopped.")

# ----------- STEP-UP AND STEP-DOWN TERRAIN CONTROL ------------
func local_move():
	# Stepping mechanics
	_step_up()
	_step_down()
	was_grounded = is_on_floor()  # Track grounded state

func _step_up():
	var result = PhysicsTestMotionResult3D.new()
	var parameters = PhysicsTestMotionParameters3D.new()
	parameters.from = global_transform
	parameters.motion = Vector3.UP * _step_height

	if PhysicsServer3D.body_test_motion(get_rid(), parameters, result):
		global_transform = global_transform.translated(result.get_remainder())

func _step_down():
	if was_grounded or local_velocity.y > 0 or is_on_floor():
		return

	var result = PhysicsTestMotionResult3D.new()
	var parameters = PhysicsTestMotionParameters3D.new()
	parameters.from = global_transform
	parameters.motion = Vector3.DOWN * _step_height

	if PhysicsServer3D.body_test_motion(get_rid(), parameters, result):
		global_transform = global_transform.translated(result.get_travel())


character facing backwards when move forwards