## SettingsManager — 全局设置 + 难度（Autoload: SettingsManager）
## 权威来源：development_plan.md S6 P1（难度三档持久化）、certification_checklist.md ST-03（清除本地数据）
## 持久化 user://save_data.json：{ difficulty:int, locale:String, master_volume:float }
extends Node

const SAVE_PATH: String = "user://save_data.json"

enum Difficulty { BEGINNER = 0, STANDARD = 1, CHALLENGE = 2 }

var difficulty:     int   = Difficulty.STANDARD
var locale:         String = "zh_CN"
var master_volume:  float = 1.0
var ftue_done:      bool = false   # 首次启动难度引导是否已完成（FTUE）

func _ready() -> void:
	_load()
	_load_balance()
	_apply_audio()
	if Locale:
		Locale.set_locale(locale)

## 是否需要首启难度引导（FTUE）
func needs_ftue() -> bool:
	return not ftue_done

## 完成 FTUE：设定难度并持久化标志
func complete_ftue(chosen_difficulty: int) -> void:
	difficulty = clampi(chosen_difficulty, 0, 2)
	ftue_done = true
	_save()

## 难度乘子 + Boss 血量：纯数据驱动，唯一来源 data/balance.json（游戏设计器「平衡」面板维护）
## get() 的默认仅为防崩溃，非内容副本
var _MULTS: Dictionary = {}    # key → [初学者, 标准, 挑战]
var _BOSS_HP: Dictionary = {}  # 原型→基础血量
var _AETHER_THRESHOLDS: Array = []   # [[到达波次, 碎片], ...] 升序
var _MANA_HEAT: Dictionary = {"per_cast": 0.01, "decay_per_sec": 0.1, "max": 1.0}
var _INFINITE: Dictionary = {"hp_per_shot": 1.0, "hp_cost_cap_ratio": 0.15}

const BALANCE_JSON: String = "res://data/balance.json"

func _mult(key: String) -> float:
	var arr: Array = _MULTS.get(key, [1.0, 1.0, 1.0])
	return float(arr[clampi(difficulty, 0, arr.size() - 1)])

func enemy_hp_mult() -> float:        return _mult("enemy_hp")
func player_dmg_taken_mult() -> float: return _mult("player_dmg")
func wave_count_mult() -> float:       return _mult("wave_count")
func boss_hp_mult() -> float:          return _mult("boss_hp")

## Boss 基础血量（WaveManager._spawn_boss 查询；balance.json 可调）
func get_boss_base_hp(boss_type: int) -> float:
	return float(_BOSS_HP.get(str(boss_type), 500.0))

## 到达波次 → 以太碎片（阶梯查表：取"波次阈值 ≤ wave"的最高阶梯值；升序表）
func aether_for_wave(wave: int) -> int:
	var table: Array = _AETHER_THRESHOLDS
	if table.is_empty():
		table = [[1, 0], [5, 3], [10, 8], [15, 14], [20, 20]]
	var result: int = 0
	for entry in table:
		if entry is Array and entry.size() >= 2 and int(entry[0]) <= wave:
			result = maxi(result, int(entry[1]))   # 取 ≤wave 阶梯中的最大值，容忍无序表（设计师手填不必排序）
	return result

func mana_heat_per_cast() -> float:   return float(_MANA_HEAT.get("per_cast", 0.01))
func mana_heat_decay() -> float:      return float(_MANA_HEAT.get("decay_per_sec", 0.1))
func mana_heat_max() -> float:        return float(_MANA_HEAT.get("max", 1.0))
func infinite_hp_per_shot() -> float: return float(_INFINITE.get("hp_per_shot", 1.0))
func hp_cost_cap_ratio() -> float:    return float(_INFINITE.get("hp_cost_cap_ratio", 0.15))

## 数据驱动：加载 balance.json（难度乘子 + Boss 血量）
func _load_balance() -> void:
	if not FileAccess.file_exists(BALANCE_JSON):
		push_error("SettingsManager: 缺失 res://data/balance.json（难度乘子/Boss血量）")
		return
	var data = JSON.parse_string(FileAccess.get_file_as_string(BALANCE_JSON))
	if not (data is Dictionary):
		push_error("SettingsManager: balance.json 格式错误")
		return
	if data.has("difficulty_mults") and data["difficulty_mults"] is Dictionary:
		for k in data["difficulty_mults"]:
			_MULTS[k] = data["difficulty_mults"][k]
	if data.has("boss_hp") and data["boss_hp"] is Dictionary:
		_BOSS_HP = data["boss_hp"]
	if data.has("aether_thresholds") and data["aether_thresholds"] is Array:
		_AETHER_THRESHOLDS = data["aether_thresholds"]
	if data.has("mana_heat") and data["mana_heat"] is Dictionary:
		for k in data["mana_heat"]:
			_MANA_HEAT[k] = data["mana_heat"][k]
	if data.has("infinite_spells") and data["infinite_spells"] is Dictionary:
		for k in data["infinite_spells"]:
			_INFINITE[k] = data["infinite_spells"][k]

func difficulty_name() -> String:
	return ["DIFF_BEGINNER", "DIFF_STANDARD", "DIFF_CHALLENGE"][difficulty]

# ── 设置变更（即时保存）──────────────────────────────────────
func set_difficulty(d: int) -> void:
	difficulty = clampi(d, 0, 2)
	_save()

func cycle_difficulty() -> int:
	set_difficulty((difficulty + 1) % 3)
	return difficulty

func set_locale(loc: String) -> void:
	locale = loc
	if Locale:
		Locale.set_locale(loc)
	_save()

func set_master_volume(v: float) -> void:
	master_volume = clampf(v, 0.0, 1.0)
	_apply_audio()
	_save()

func _apply_audio() -> void:
	var bus: int = AudioServer.get_bus_index("Master")
	if bus >= 0:
		AudioServer.set_bus_volume_db(bus, linear_to_db(maxf(0.0001, master_volume)))

# ── ST-03：清除所有本地数据 ─────────────────────────────────
func clear_all_local_data() -> void:
	var files: Array = [
		"user://crash_log.txt", "user://crash_log_prev.txt",
		"user://run_a.json", "user://run_b.json",
		"user://endless_records.json", "user://save_data.json",
	]
	for path in files:
		if FileAccess.file_exists(path):
			DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
	_remove_dir_recursive("user://runs")
	# 重置内存态
	difficulty = Difficulty.STANDARD
	master_volume = 1.0
	ftue_done = false   # 清数据后重新触发首启难度引导
	# 用路径引用避免编译时 autoload 依赖（打破 SettingsManager→ProfileManager→WaveManager→SettingsManager 循环）
	var pm: Node = get_node_or_null("/root/ProfileManager")
	if pm and pm.has_method("clear_run"):
		pm.clear_run()
	MetaProgress.clear()
	_apply_audio()

func _remove_dir_recursive(path: String) -> void:
	var d := DirAccess.open(path)
	if d == null:
		return
	d.list_dir_begin()
	var fn := d.get_next()
	while fn != "":
		if not d.current_is_dir():
			DirAccess.remove_absolute(ProjectSettings.globalize_path(path + "/" + fn))
		fn = d.get_next()
	d.list_dir_end()
	DirAccess.remove_absolute(ProjectSettings.globalize_path(path))

# ── 持久化 ──────────────────────────────────────────────────
func _save() -> void:
	var data: Dictionary = {
		"difficulty": difficulty, "locale": locale,
		"master_volume": master_volume, "ftue_done": ftue_done,
	}
	var f := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
	if f:
		f.store_string(JSON.stringify(data))
		f.close()

func _load() -> void:
	if not FileAccess.file_exists(SAVE_PATH):
		return
	var parsed = JSON.parse_string(FileAccess.get_file_as_string(SAVE_PATH))
	if not (parsed is Dictionary):
		return
	difficulty = clampi(int(parsed.get("difficulty", 1)), 0, 2)
	locale = String(parsed.get("locale", "zh_CN"))
	master_volume = clampf(float(parsed.get("master_volume", 1.0)), 0.0, 1.0)
	ftue_done = bool(parsed.get("ftue_done", false))
