## ProfileManager — 局内 Run 存档 A/B 双槽（Autoload: ProfileManager）
## 权威来源：architecture_design.md ADR-A2
## 下面实现了：
##   - A/B 双槽交替写入防崩溃
##   - schema_version 字段必须
##   - 所有读取必须经过 _migrate_run()
##   - NOTIFICATION_WM_CLOSE_REQUEST 尽力写最后一笔
extends Node

const SCHEMA_VERSION: int = 2  # v2：新增 wand（Core/deck/bench）持久化
const PATH_A: String = "user://run_a.json"
const PATH_B: String = "user://run_b.json"

var _current_slot: String = PATH_A  # 上次写入的槽
var _dirty:        bool   = false

## 法杖状态提供者（CombatManager 在 _ready 注册）；须有 get_wand_save_data/apply_wand_save_data
var _wand_provider: Object = null

func set_wand_provider(p: Object) -> void:
	_wand_provider = p

func _ready() -> void:
	get_tree().root.connect("close_requested", _on_close_request)

func _on_close_request() -> void:
	if _dirty:
		save_run()
		_dirty = false

## 写入当前 Run 状态（交替槽位）
func save_run() -> void:
	var data: Dictionary = _collect_run_data()
	data["schema_version"] = SCHEMA_VERSION
	data["saved_at"] = Time.get_unix_time_from_system()
	var json_str: String = JSON.stringify(data)
	var target: String = _next_slot()
	var file := FileAccess.open(target, FileAccess.WRITE)
	if file:
		file.store_string(json_str)
		file.close()
		_current_slot = target
		_dirty = false

## 加载 Run，自动选择未损坏的最新槽
func load_run() -> Dictionary:
	var data_a: Dictionary = _read_slot(PATH_A)
	var data_b: Dictionary = _read_slot(PATH_B)
	var best: Dictionary
	if data_a.is_empty() and data_b.is_empty():
		return {}
	elif data_a.is_empty():
		best = data_b
	elif data_b.is_empty():
		best = data_a
	else:
		var ta: int = int(data_a.get("saved_at", 0))
		var tb: int = int(data_b.get("saved_at", 0))
		best = data_a if ta > tb else data_b
	return _migrate_run(best)

## 应用局内状态
func apply_run(data: Dictionary) -> void:
	if data.is_empty():
		return
	PlayerStats.load_save_data(data.get("player_stats", {}))
	WaveManager.current_wave = int(data.get("wave_num", 0))
	var shop_seed: int = int(data.get("shop_seed", 0))
	if shop_seed > 0:
		ShopManager._shop_seed = shop_seed
	# 恢复法杖（Core/deck/bench）
	if data.has("wand") and is_instance_valid(_wand_provider) and _wand_provider.has_method("apply_wand_save_data"):
		_wand_provider.apply_wand_save_data(data.get("wand", {}))

## 清除 Run（死亡 / 新开局）
func clear_run() -> void:
	for path in [PATH_A, PATH_B]:
		if FileAccess.file_exists(path):
			DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
	_dirty = false

func mark_dirty() -> void:
	_dirty = true

func _next_slot() -> String:
	return PATH_B if _current_slot == PATH_A else PATH_A

func _collect_run_data() -> Dictionary:
	var d: Dictionary = {
		"wave_num":     WaveManager.current_wave,
		"shop_seed":    ShopManager.get_shop_seed(),
		"player_stats": PlayerStats.get_save_data(),
	}
	if is_instance_valid(_wand_provider) and _wand_provider.has_method("get_wand_save_data"):
		d["wand"] = _wand_provider.get_wand_save_data()
	return d

func _read_slot(path: String) -> Dictionary:
	if not FileAccess.file_exists(path):
		return {}
	var text: String = FileAccess.get_file_as_string(path)
	if text.is_empty():
		return {}
	var parsed = JSON.parse_string(text)
	if parsed is Dictionary:
		return parsed
	return {}

## 迁移旧存档
func _migrate_run(data: Dictionary) -> Dictionary:
	var ver: int = int(data.get("schema_version", 0))
	if ver < 1:
		# v0→v1: 无破坏性字段变更
		data["schema_version"] = 1
	if ver < 2:
		# v1→v2: 旧档无 wand 字段；apply_run 检测缺失即保留默认法杖，无需补字段
		data["schema_version"] = 2
	return data
