class_name Player
extends CharacterBody2D

@onready var death_sfx = preload("res://scenes/assets/jingles/fail.wav")
@onready var raycast: RayCast2D = $RayCast2D
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var map: MainLevel = get_tree().root.get_child(0).get_node("TileMap")
@onready var tile_map: TileMapLayer = get_tree().root.get_child(0).get_node("TileMap/TileMapLayer")
@onready var camera: Camera2D = $AnimatedSprite2D/Camera2D
@onready var audio_player: AudioStreamPlayer2D = $AudioStreamPlayer2D
@onready var start_point: Node2D = get_tree().root.get_child(0).get_node("Start")

const TILE_SIZE = 8

@export var speed: float = 40
@export var checkpoints: Array[Node2D]
@export var current_checkpoint := 0

var is_moving = false
var should_move = false
var is_dead = false
var input_dir: Vector2
var respawn_time: int = 10
var can_teleport := true
var rng := RandomNumberGenerator.new()
var camera_shake: float = 0.0
var camera_shake_fade: float = 0.0
var attack_frame := 0
var lock_for_end := false
var end_tick := 0

func _ready():
	_respawn()
	pass

func _process(delta):
	if not is_dead:
		_try_get_input()
		_try_interact()
		_shake_camera(delta)
	if is_dead:
		if respawn_time > 0:
			respawn_time -= 1
		elif respawn_time <= 0:
			_respawn()
	
func _physics_process(delta):
	_try_move(delta)
	
func _respawn():
	sprite.stop()
	sprite.frame = 0
	if checkpoints.size() > 0 and checkpoints[current_checkpoint]:
		position = checkpoints[current_checkpoint].position
	else:
		position = start_point.position
	sprite.global_position = position
	is_dead = false
	is_moving = false
	should_move = false

func _try_get_input():
	should_move = false
	if not lock_for_end:
		if Input.is_action_pressed("move_up"):
			input_dir = Vector2.UP
			should_move = true
		elif Input.is_action_pressed("move_right"):
			input_dir = Vector2.RIGHT
			should_move = true
		elif Input.is_action_pressed("move_down"):
			input_dir = Vector2.DOWN
			should_move = true
		elif Input.is_action_pressed("move_left"):
			input_dir = Vector2.LEFT
			should_move = true
	else:
		input_dir = Vector2.UP
		should_move = true
		end_tick += 1
	
	if end_tick > 250:
		animation_player.play(&"end")

	_change_sprite()
	
	if attack_frame > 0:
		attack_frame -= 1
	
	if not should_move and not is_moving and attack_frame <= 0:
		sprite.frame = 0
		sprite.stop()
	
	if should_move and not is_moving:
		sprite.play()
		raycast.target_position = input_dir * 8
		raycast.force_raycast_update()
		
		if raycast.is_colliding():
			return
			
		var current_tile: Vector2i = tile_map.local_to_map(global_position)
		var target_tile: Vector2i = Vector2i(
			current_tile.x + input_dir.x, 
			current_tile.y + input_dir.y
		)

		global_position = tile_map.map_to_local(target_tile)

		var is_death_tile = map.is_death_tile(global_position)
		if is_death_tile:
			die()
			return
			
		is_moving = true
		sprite.global_position = tile_map.map_to_local(current_tile)

func _change_sprite():
	var is_attacking = attack_frame > 0
	if not is_attacking:
		if input_dir == Vector2.UP:
			sprite.animation = &"move_up"
		elif input_dir == Vector2.RIGHT:
			sprite.animation = &"move_right"
			sprite.flip_h = false
		elif input_dir == Vector2.DOWN:
			sprite.animation = &"move_down"
		elif input_dir == Vector2.LEFT:
			sprite.animation = &"move_right"
			sprite.flip_h = true

func _try_move(delta: int):
	if not is_moving or is_dead:
		return
	
	sprite.global_position = sprite.global_position.move_toward(global_position, 0.5)
	
	if global_position == sprite.global_position:
		is_moving = false
		return

func die():
	is_dead = true
	is_moving = false
	respawn_time = 10
	audio_player.stream = death_sfx
	audio_player.play()

func apply_camera_shake(strength: float = 10.0, fade: float = 5.0):
	camera_shake = strength
	camera_shake_fade = fade

func _try_interact():
	if lock_for_end:
		return
		
	raycast.target_position = input_dir * 8
	raycast.force_raycast_update()

	if Input.is_action_just_pressed("attack"):
		sprite.play(&"spin")
		attack_frame = 50
		var collider = raycast.get_collider()
		if collider and collider is PuzzleElement:
			(collider as PuzzleElement).interact(self)

func teleport(pos: Vector2i):
	global_position = pos
	can_teleport = false

func _shake_camera(delta):
	if camera_shake > 0:
		camera_shake = lerpf(camera_shake, 0, camera_shake_fade * delta)
		camera.offset = Vector2(rng.randf_range(-camera_shake, camera_shake), rng.randf_range(-camera_shake, camera_shake))

func _on_area_entered(area: Area2D):
	if area.is_in_group("death"):
		die()

func _on_animation_player_animation_finished(anim_name: String):
	if anim_name == &"end":
		get_tree().quit()
