class_name Boss
extends CharacterBody2D

@export var speed : float = 170.0
@export var boss_fight_cutscenes : BossFightCutscenes
@export var phases : int = 2
@export var max_health : int = 10

@onready var boss_behavior : BossBehavior = $BossBehavior
@export var exit_door_interactable : Interactable
@onready var animation_player: AnimationPlayer = $AnimationPlayer

var map_loader : MapLoader

var anchor_state : Dictionary = {}
var boss_anchor_state : Dictionary = {}

var player : Player
var player_input_recorder : InputRecorder

var motion_velocity : Vector2 = Vector2.ZERO
var sprite_direction : Vector2 = Vector2.ZERO

var boss_anchor = preload("res://scenes/nodes/boss/boss_remnant/boss_anchor.tscn")

@onready var time_shift: TimeShift = $TimeShift

func _ready() -> void: 
	animation_player.play("RESET")
	boss_behavior.disable_force_field()
	SignalBus.boss_changed_health_absolute.connect(func (abs_health) : take_damage(max_health-abs_health))
	SignalBus.boss_changed_health_relative.connect(take_damage)
	motion_mode = CharacterBody2D.MOTION_MODE_FLOATING  # s.t. when pushing e.g. a box from the top, it works
	boss_behavior.set_health(max_health)
	
	if get_parent().get_parent() is MapLoader:
		map_loader = get_parent().get_parent()
		player = map_loader.player
		player_input_recorder = map_loader.input_recorder
		time_shift.map_loader = map_loader
	
	SignalBus.set_anchor.connect(save_state)
	SignalBus.reload_anchor.connect(reload_state)
	SignalBus.boss_set_anchor.connect(boss_save_state)
	SignalBus.boss_reload_anchor.connect(boss_reload_state)
	
	boss_fight_cutscenes.intro_cutscene()

func _physics_process(_delta: float) -> void:
	if Globals.paused or SceneChanger.mid_change or SceneChanger.mid_animation:
		return
	velocity = motion_velocity
	move_and_slide()

func start_bossfight():
	if not SceneChanger.mid_animation:
		SceneChanger.mid_animation = true
		if is_inside_tree():
			await get_tree().create_timer(0.5).timeout 
	if time_shift.map_loader:
		time_shift._set_timecheck_position()
	var anchor = boss_anchor.instantiate()
	if get_parent():
		get_parent().add_child(anchor)
		anchor.global_position = global_position
	AudioManager.create_audio(SoundEffect.SOUND_EFFECT_TYPE.SET_ANCHOR)
	if is_inside_tree():
		await get_tree().create_timer(0.5).timeout 
	boss_behavior.enable_force_field()
	if is_inside_tree():
		await get_tree().create_timer(0.25).timeout 
	SceneChanger.mid_animation = false
	boss_behavior.start_bossfight()

func take_damage(damage : int):
	boss_behavior.take_damage(damage)

func stun(duration : float = 1.0):
	boss_behavior.get_stunned(duration)
	AudioManager.create_audio(SoundEffect.SOUND_EFFECT_TYPE.BOSS_STUNNED)

func time_travel():
	boss_behavior.force_field.hide()
	animation_player.play("timeshift")
	time_shift.trigger()
	AudioManager.create_audio(SoundEffect.SOUND_EFFECT_TYPE.BOSS_TIME_SHIFT)
	await animation_player.animation_finished
	boss_behavior.state_chart.send_event("changed_time")
	boss_behavior._reactivate_timeshifting()
	boss_behavior.force_field.show()

func die():
	SignalBus.end_boss_fight()
	boss_behavior.hide_ui()
	if is_inside_tree():
		await get_tree().create_timer(1).timeout #animation placeholder
	exit_door_interactable.switch_state()
	queue_free()

#region Movement
func get_closest_player_base_position() -> Vector2:
	var closest : PlayerBase = null
	if player:
		closest = player.player_base
	if player_input_recorder and player_input_recorder.anchored_remnants:
		for anchored_remnant in player_input_recorder.anchored_remnants:
			if anchored_remnant and anchored_remnant is AnchoredRemnant and (not closest or global_position.distance_to(closest.global_position) > global_position.distance_to(anchored_remnant.player_base.global_position)):
				closest = anchored_remnant.player_base
	return closest.global_position if closest else global_position
#endregion

#region Anchored Remnant
func save_state():
	anchor_state = {
		"global_position" = global_position,
	}
	boss_behavior.save_state()

func reload_state():
		global_position = anchor_state.get("global_position", global_position)
		boss_behavior.reload_state()
#endregion

#region Boss Fight Phase Management
func boss_save_state():
	boss_anchor_state = {
		"global_position" = global_position
	}

func boss_reload_state():
	boss_behavior.state_chart.send_event("next_phase")
	global_position = boss_anchor_state.get("global_position", global_position)
	boss_behavior.set_health(max_health)
	await SignalBus.changed_player_position
	boss_behavior.reset_for_phase()
#endregion
