extends SceneTree

const FishCatchType = preload("res://fish/fish_catch.gd")
const FishExperienceType = preload("res://fish/fish_experience.gd")
const FishQualityType = preload("res://fish/fish_quality.gd")
const PlayerExperienceType = preload(
	"res://progression/player_experience.gd"
)
const Catalog: FishPool = preload("res://fish/pools/fish_catalog.tres")


func _initialize() -> void:
	call_deferred("_run")


func _run() -> void:
	_validate_level_curve()
	_validate_catch_awards()
	_validate_player_experience_state()
	_validate_save_migration()
	print("Player experience validation: PASS")
	quit()


func _validate_level_curve() -> void:
	assert(PlayerExperienceType.experience_required_for_next_level(1) == 100)
	assert(PlayerExperienceType.experience_required_for_next_level(2) == 130)
	assert(PlayerExperienceType.experience_required_for_next_level(3) == 170)
	assert(PlayerExperienceType.total_experience_for_level(1) == 0)
	assert(PlayerExperienceType.total_experience_for_level(2) == 100)
	assert(PlayerExperienceType.total_experience_for_level(3) == 230)
	assert(PlayerExperienceType.total_experience_for_level(4) == 400)
	assert(PlayerExperienceType.level_for_total_experience(99) == 1)
	assert(PlayerExperienceType.level_for_total_experience(100) == 2)
	assert(PlayerExperienceType.level_for_total_experience(399) == 3)
	assert(PlayerExperienceType.level_for_total_experience(400) == 4)
	assert(
		is_equal_approx(
			PlayerExperienceType.progress_for_total_experience(50),
			0.5,
		)
	)


func _validate_catch_awards() -> void:
	var minimum_boring: FishCatch = _make_catch(
		FishQualityType.Tier.BORING,
		false,
	)
	assert(
		FishExperienceType.calculate_catch_experience(
			minimum_boring,
			false,
			0,
		) == 50
	)
	var maximum_shiny: FishCatch = _make_catch(
		FishQualityType.Tier.SHINY,
		true,
	)
	var boring_mask: int = FishQualityType.bit_for(
		FishQualityType.Tier.BORING
	)
	assert(
		FishExperienceType.calculate_catch_experience(
			maximum_shiny,
			true,
			boring_mask,
		) == 46
	)
	var almost_mastered: int = (
		FishQualityType.ALL_TIERS_MASK
		& ~FishQualityType.bit_for(FishQualityType.Tier.SHINY)
	)
	assert(
		FishExperienceType.calculate_catch_experience(
			maximum_shiny,
			true,
			almost_mastered,
		) == 146
	)
	assert(
		FishExperienceType.calculate_catch_experience(
			maximum_shiny,
			true,
			FishQualityType.ALL_TIERS_MASK,
		) == 31
	)


func _validate_player_experience_state() -> void:
	var experience := PlayerExperienceType.new()
	root.add_child(experience)
	var awards: Array[Array] = []
	experience.experience_awarded.connect(
		func(
			amount: int,
			previous_total: int,
			new_total: int,
			previous_level: int,
			new_level: int,
		) -> void:
			awards.append([
				amount,
				previous_total,
				new_total,
				previous_level,
				new_level,
			])
	)
	assert(experience.get_level() == 1)
	assert(experience.award_experience(125))
	assert(experience.get_total_experience() == 125)
	assert(experience.get_level() == 2)
	assert(experience.get_experience_in_level() == 25)
	assert(awards.size() == 1)
	assert(awards[0] == [125, 0, 125, 1, 2])
	assert(experience.restore_total_experience(400))
	assert(experience.get_level() == 4)
	assert(not experience.restore_total_experience(-1))
	experience.queue_free()


func _validate_save_migration() -> void:
	var manager := PlayerSaveManager.new()
	root.add_child(manager)
	var version_five: Dictionary = {
		"save_version": 5,
		"wallet": {"balance": 0},
		"collection": {
			"discovered_fish_ids": [],
			"discovered_quality_masks": {},
		},
		"inventory": {"next_catch_sequence": 1, "catches": []},
		"bag": {"items": []},
		"hotbar": {"selected_slot": 0, "slots": []},
		"upgrades": {"reel_speed_level": 0, "barrier_power_level": 0},
		"cooler": {"capacity_level": 0},
		"art": {"unlock_mask": 0},
	}
	var migrated: Dictionary = manager.call(
		"_migrate_save",
		version_five,
		5,
	)
	assert(int(migrated.get("save_version", -1)) == 7)
	var experience_data: Dictionary = migrated.get("experience", {})
	assert(int(experience_data.get("total_experience", -1)) == 0)
	var world_data: Dictionary = migrated.get("world", {})
	assert(is_equal_approx(float(world_data.get("time_hours", -1.0)), 8.0))
	assert(
		PlayerJobService.validate_save_data(migrated.get("jobs", {}))
	)
	manager.queue_free()


func _make_catch(quality: int, maximum_weight: bool) -> FishCatch:
	var fish: FishData = Catalog.get_fish_by_id(&"bluegill")
	assert(fish != null)
	var fish_catch := FishCatchType.new()
	fish_catch.fish = fish
	fish_catch.fish_id = fish.id
	fish_catch.catch_id = StringName("bluegill:xp-%d-%s" % [
		quality,
		"max" if maximum_weight else "min",
	])
	fish_catch.catch_sequence = 1
	fish_catch.weight_lb = (
		fish.get_maximum_weight()
		if maximum_weight
		else fish.get_minimum_weight()
	)
	fish_catch.display_scale = fish.get_display_scale_for_weight(
		fish_catch.weight_lb
	)
	fish_catch.quality = quality
	fish_catch.sale_value = FishQualityType.apply_sale_value(
		fish.get_sale_value_for_weight(fish_catch.weight_lb),
		quality,
	)
	return fish_catch
