class_name LocalPlayerMovementController
extends RefCounted

## Scene-free state and velocity integrator for the current local sandbox player.
## Direction inputs use a Godot-world movement basis selected by the scene
## adapter. This class does not discover Nodes, query terrain or mutate transforms.

var godot_world_velocity_units_per_second: Vector3:
	get:
		return _godot_world_velocity_units_per_second

var is_flight_enabled: bool:
	get:
		return _is_flight_enabled

var movement_profile_id: StringName:
	get:
		return _movement_capabilities.profile_id

var _run_speed_units_per_second: float
var _backward_speed_units_per_second: float
var _strafe_speed_units_per_second: float
var _flight_vertical_speed_units_per_second: float
var _acceleration_units_per_second_squared: float
var _sandbox_sprint_multiplier: float
var _keyboard_turn_speed_radians_per_second: float
var _movement_capabilities: PlayerMovementCapabilities
var _godot_world_velocity_units_per_second := Vector3.ZERO
var _is_flight_enabled := false


## Creates a movement state with explicit Godot-unit speeds, acceleration and capabilities.
## The current compatibility renderer treats one Godot unit as one WoW yard.
## A null capability input fails closed to Blizzlike335 debug permissions.
func _init(
		run_speed_units_per_second: float,
		backward_speed_units_per_second: float,
		strafe_speed_units_per_second: float,
		flight_vertical_speed_units_per_second: float,
		acceleration_units_per_second_squared: float,
		sandbox_sprint_multiplier: float,
		movement_capabilities: PlayerMovementCapabilities = null,
		keyboard_turn_speed_radians_per_second: float = PI
) -> void:
	_run_speed_units_per_second = run_speed_units_per_second
	_backward_speed_units_per_second = backward_speed_units_per_second
	_strafe_speed_units_per_second = strafe_speed_units_per_second
	_flight_vertical_speed_units_per_second = flight_vertical_speed_units_per_second
	_acceleration_units_per_second_squared = acceleration_units_per_second_squared
	_sandbox_sprint_multiplier = sandbox_sprint_multiplier
	_keyboard_turn_speed_radians_per_second = maxf(
		keyboard_turn_speed_radians_per_second,
		0.0
	)
	_movement_capabilities = (
		movement_capabilities
		if movement_capabilities != null
		else PlayerMovementCapabilities.blizzlike_335()
	)


## Returns the Godot yaw delta for one signed keyboard-turn request.
## Positive intent turns right, which is negative rotation around Godot +Y.
func calculate_yaw_delta_radians(move_intent: MoveIntent, delta_seconds: float) -> float:
	return (
		-move_intent.turn_axis
		* _keyboard_turn_speed_radians_per_second
		* maxf(delta_seconds, 0.0)
	)


## Toggles sandbox free flight and clears velocity when the profile permits it.
## Returns the resulting state; rejected compatibility-profile requests return false.
func toggle_sandbox_flight() -> bool:
	if not _movement_capabilities.allows_debug_free_flight:
		return false
	_is_flight_enabled = not _is_flight_enabled
	_godot_world_velocity_units_per_second = Vector3.ZERO
	return _is_flight_enabled


## Advances velocity by one physics step and returns local-world displacement.
## `godot_world_movement_basis` is the player basis on ground and camera-pivot basis in flight.
## Negative delta values are treated as zero and do not mutate velocity.
func advance(
		move_intent: MoveIntent,
		godot_world_movement_basis: Basis,
		delta_seconds: float
) -> Vector3:
	var safe_delta_seconds := maxf(delta_seconds, 0.0)
	if is_zero_approx(safe_delta_seconds):
		return Vector3.ZERO

	var target_velocity := _calculate_target_velocity(move_intent, godot_world_movement_basis)
	_godot_world_velocity_units_per_second = _godot_world_velocity_units_per_second.move_toward(
		target_velocity,
		_acceleration_units_per_second_squared * safe_delta_seconds
	)
	return _godot_world_velocity_units_per_second * safe_delta_seconds


func _calculate_target_velocity(move_intent: MoveIntent, godot_world_movement_basis: Basis) -> Vector3:
	var forward_direction := -godot_world_movement_basis.z
	var right_direction := godot_world_movement_basis.x
	if not _is_flight_enabled:
		forward_direction.y = 0.0
		right_direction.y = 0.0
	forward_direction = forward_direction.normalized()
	right_direction = right_direction.normalized()

	var forward_speed := (
		_run_speed_units_per_second
		if move_intent.forward_axis > 0.0
		else _backward_speed_units_per_second
	)
	var speed_multiplier := (
		_sandbox_sprint_multiplier
		if move_intent.is_sprint_requested and _movement_capabilities.allows_debug_sprint
		else 1.0
	)
	var target_velocity := (
		forward_direction * move_intent.forward_axis * forward_speed
		+ right_direction * move_intent.strafe_axis * _strafe_speed_units_per_second
	) * speed_multiplier
	if _is_flight_enabled:
		target_velocity.y += (
			move_intent.vertical_axis
			* _flight_vertical_speed_units_per_second
			* speed_multiplier
		)
	return target_velocity
