extends Base
## Achievement handler to check and unlock achievements
##
## An achievement handler that will check the available achievements defined in
## both the game files and the mods. When called with triggers will check to see
## if that trigger value unlocks achievements.

signal achievement_unlocked(achievement: String)

const CACHE_TIME = 60000
const POPUP_POP_TIME = 1000
const POPUP_LINGER_TIME = 5000
const POPUP_HIDE_TIME = 1000
const POPUP_BREAK = 1000
const POPUP_HOVER_TIME = 250

@onready var popup = $"CanvasLayer/Popup"
@onready var popup_title = $"CanvasLayer/Popup/Popup/Title"
@onready var popup_body = $"CanvasLayer/Popup/Popup/Body"
@onready var audio = $"AudioStreamPlayer"
@onready var audio2 = $"AudioStreamPlayer2"

enum AchievementState {
	ENTERING,
	SHOWN,
	HOVERED,
	EXITING,
	HIDDEN
}

var queue = []
var cached_percents = {}
var last_percent_pull: int
var showing_popup = false
var starting_popup_pos
var starting_popup_size
var popup_tween
var popup_timer = 0
var popup_time = 6
#var entering = false
#var exiting = false
#var hovered = false

var achievement_state = AchievementState.HIDDEN
var hovered = false



func _ready():
	#_log_category = "ACH"
	#_log_icon = "res://components/Logger/scroll-text.svg"
	#_log_color = "#a166a1"
	super()
	_info("Achievements is active")
	starting_popup_pos = popup.position
	starting_popup_size = popup.size
	popup.position.x = starting_popup_pos.x + starting_popup_size.x
	popup.modulate = Color.TRANSPARENT
	popup.scale = Vector2(0.5, 0.5)
	if _triggerer:
		_triggerer.listen("any", _on_trigger)


func _on_trigger(data: Dictionary) -> void:
	_check_trigger(data.trigger, "Trigger")


func _process(delta):
	if Persister.get_value("paused") or not Persister.get_value("active"):
		return
	popup_timer += delta
	
	if popup_timer > popup_time and achievement_state == AchievementState.SHOWN:
		_exit_popup()
		
	
	if not queue.is_empty() and not showing_popup:
		_show_popup(queue.pop_front())
	
	if popup_tween and not popup_tween.is_running():
		if hovered and achievement_state == AchievementState.SHOWN:
			achievement_state = AchievementState.HOVERED
			
			popup_tween = create_tween()
			popup_tween.set_parallel()
			popup_tween.set_trans(Tween.TRANS_QUAD)
			popup_tween.tween_property(popup, "scale", Vector2(1.05, 1.05), float(POPUP_HOVER_TIME) / 1000)
			popup_tween.tween_property(popup, "position:x", starting_popup_pos.x - 20, float(POPUP_HOVER_TIME) / 1000)
		if not hovered and achievement_state == AchievementState.HOVERED:
			achievement_state = AchievementState.SHOWN
			
			popup_tween = create_tween()
			popup_tween.set_parallel()
			popup_tween.set_trans(Tween.TRANS_QUAD)
			popup_tween.tween_property(popup, "scale", Vector2(1, 1), float(POPUP_HOVER_TIME) / 1000)
			popup_tween.tween_property(popup, "position:x", starting_popup_pos.x, float(POPUP_HOVER_TIME) / 1000)


func _exit_popup():
	achievement_state = AchievementState.EXITING
	audio2.play()
	
	if popup_tween: popup_tween.kill()
	popup_tween = create_tween()
	
	popup_tween.set_ease(Tween.EASE_IN)
	popup_tween.set_trans(Tween.TRANS_BACK)
	popup_tween.set_parallel()
	popup_tween.tween_property(popup, "modulate", Color.TRANSPARENT, POPUP_POP_TIME / 1000)
	popup_tween.tween_property(popup, "scale", Vector2(0.5, 0.5), POPUP_POP_TIME / 1000)
	popup_tween.tween_property(popup, "position:x", starting_popup_pos.x + starting_popup_size.x, POPUP_HIDE_TIME / 1000)
	popup_tween.tween_interval(POPUP_BREAK / 1000)
	popup_tween.chain()
	popup_tween.tween_callback(func(): showing_popup = false)
	
	_info("Hiding achievement popup")


func _on_data_persisted(key: String, value, category: PersisterEnums.Scope):
	_check_trigger(key, "Data", value)


## Get all of the achievement info
func get_achievements() -> Dictionary:
	if not _data:
		if (_logger): _logger.warn("Could not find data autoload when getting achievements")
		return {}
	
	if not _data.data.has("achievements"):
		if (_logger): _logger.warn("Could not find achievement data")
		return {}
	
	var local_ach_data = _data.data.achievements.duplicate()
	
	var percents = _get_achievement_percents()
	
	for achievement in _data.data.achievements:
		if percents.keys().has(achievement):
			local_ach_data.percent = percents[achievement]
	
	return _data.data.achievements


## Get achievement info given the achievement slug
func get_achievement(achievement: String) -> Dictionary:
	if not _data:
		if _logger: _logger.warn("Could not find data autoload when getting achievement %s" % [achievement])
		return {}
	
	if not _data.data.has("achievements"):
		if _logger: _logger.warn("Could not find achievement data")
		return {}
	
	if not _data.data.achievements.has(achievement):
		if _logger: _logger.warn("Could not find achievement %s" % [achievement])
		return {}
	
	var achievement_data = _data.data.achievements[achievement].duplicate()
	
	# TODO: Add in achievement percent
	
	return achievement_data


func _show_popup(achievement: String) -> void:
	var achievement_data = get_achievement(achievement)
	achievement_state = AchievementState.ENTERING
	popup_timer = 0
	
	if achievement_data.is_empty():
		if _logger: _logger.error("Could not get achievement %s info when trying to show" % [achievement])
		return
	
	popup_title.text = achievement_data.name
	popup_body.text = achievement_data.description
	
	showing_popup = true
	popup.position.x = starting_popup_pos.x + starting_popup_size.x
	popup.modulate = Color.TRANSPARENT
	popup.scale = Vector2(0.5, 0.5)
	#popup.position.y = starting_popup_pos.y + starting_popup_size.y
	
	if popup_tween:
		popup_tween.kill()
	
	audio.play()
	
	popup_tween = create_tween()
	popup_tween.set_parallel()
	popup_tween.set_ease(Tween.EASE_OUT)
	popup_tween.set_trans(Tween.TRANS_BACK)
	popup_tween.tween_property(popup, "modulate", Color.WHITE, POPUP_POP_TIME / 1000)
	popup_tween.tween_property(popup, "scale", Vector2(1, 1), POPUP_POP_TIME / 1000)
	popup_tween.tween_property(popup, "position:x", starting_popup_pos.x, POPUP_POP_TIME / 1000)
	popup_tween.chain()
	popup_tween.tween_callback(func():
		achievement_state = AchievementState.SHOWN
	)
	
	_info("Showing achievement %s popup" % [achievement])


func _get_achievement_percents() -> Dictionary:
	if not last_percent_pull:
		return _get_percents_from_db()
	
	if last_percent_pull + CACHE_TIME <= Time.get_ticks_msec():
		return cached_percents
	
	return _get_percents_from_db()


func _get_percents_from_db() -> Dictionary:
	last_percent_pull = Time.get_ticks_msec()
	return {}


func _send_to_db(achievement: String) -> void:
	pass


func _check_trigger(key: String, type: String, value = null):
	if not _data:
		if (_logger): _logger.warn("Could not find data autoload when checking trigger")
		return
	
	if not _data.data.has("achievements"):
		return
	
	for achievement in _data.data.achievements:
		# If the persister does not have the achievement already
		if not _persister.get_value(achievement):
			var ach_data = _data.data.achievements[achievement]
			
			var valid = false
			
			match type:
				"Data":
					if ach_data.has("key") and ach_data.key == key:
						valid = ach_data.value <= value
				"Trigger":
					if ach_data.has("trigger") and ach_data.trigger == key:
						valid = true
			
			if valid:
				achievement_unlocked.emit(achievement)
				_persister.persist_data(achievement, true, PersisterEnums.Scope.PERMANENT)
				_info("Added achievement %s to queue" % [achievement])
				queue.push_back(achievement)
				_send_to_db(achievement)


func _on_mouse_handler_hovered():
	hovered = true


func _on_mouse_handler_clicked():
	if achievement_state != AchievementState.EXITING:
		_exit_popup()


func _on_mouse_handler_unhovered():
	hovered = false