class_name SettingsMenu extends CanvasLayer
## Phase 17 — Centered modal settings panel.
##
## Layer 26: above LoadMenu (25) — shows over everything.
## Sections: Speeds (keyboard shortcut display), Auto-pause toggles,
## Audio sliders (stub), Accessibility (stub).
##
## Save → GameState.apply_settings(); Cancel → discard edits.
## Opened by TopBar's Settings button (injected by main.gd).

# ── node refs ─────────────────────────────────────────────────────────────────
var _dim: ColorRect = null
var _panel: PanelContainer = null

# Auto-pause checkboxes.
var _cb_threat:    CheckBox = null
var _cb_wanderer:  CheckBox = null
var _cb_pawn_down: CheckBox = null
var _cb_modal:     CheckBox = null

# Audio sliders.
var _sl_master:  HSlider = null
var _sl_music:   HSlider = null
var _sl_sfx:     HSlider = null
var _sl_ambient: HSlider = null

# Accessibility checkboxes.
var _cb_large_text:     CheckBox = null
var _cb_reduce_motion:  CheckBox = null


func _ready() -> void:
	layer = 26
	_build_ui()
	_set_visible(false)
	Audit.log("settings_menu", "SettingsMenu ready (layer %d)" % layer)


func _exit_tree() -> void:
	pass


# ── public API ────────────────────────────────────────────────────────────────

func open() -> void:
	_load_from_game_state()
	_set_visible(true)
	Audit.log("settings_menu", "opened")


# ── UI construction ───────────────────────────────────────────────────────────

func _build_ui() -> void:
	# Full-screen dim — stops clicks reaching the world behind.
	_dim = ColorRect.new()
	_dim.name = "Dim"
	_dim.set_anchors_preset(Control.PRESET_FULL_RECT)
	_dim.color = Color(0.0, 0.0, 0.0, 0.55)
	_dim.mouse_filter = Control.MOUSE_FILTER_STOP
	add_child(_dim)

	# Centre panel.
	_panel = PanelContainer.new()
	_panel.name = "Dialog"
	_panel.set_anchors_preset(Control.PRESET_CENTER)
	_panel.custom_minimum_size = Vector2(480, 580)
	_panel.offset_left   = -240
	_panel.offset_right  = 240
	_panel.offset_top    = -290
	_panel.offset_bottom = 290
	add_child(_panel)

	var scroll := ScrollContainer.new()
	scroll.set_anchors_preset(Control.PRESET_FULL_RECT)
	scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
	_panel.add_child(scroll)

	var vbox := VBoxContainer.new()
	vbox.add_theme_constant_override("separation", 10)
	scroll.add_child(vbox)

	# Title.
	var title := Label.new()
	title.name = "Title"
	title.text = Strings.t(&"ui.settings.title")
	title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
	vbox.add_child(title)

	_add_separator(vbox)

	# ── Speeds section ────────────────────────────────────────────────────────
	var speeds_hdr := Label.new()
	speeds_hdr.text = Strings.t(&"ui.settings.speeds")
	vbox.add_child(speeds_hdr)

	var shortcuts_lbl := Label.new()
	shortcuts_lbl.text = Strings.t(&"ui.settings.shortcuts")
	shortcuts_lbl.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
	vbox.add_child(shortcuts_lbl)

	_add_separator(vbox)

	# ── Auto-pause section ────────────────────────────────────────────────────
	var ap_hdr := Label.new()
	ap_hdr.text = Strings.t(&"ui.settings.auto_pause")
	vbox.add_child(ap_hdr)

	_cb_threat    = _make_checkbox(Strings.t(&"ui.settings.pause_threat"),    vbox)
	_cb_wanderer  = _make_checkbox(Strings.t(&"ui.settings.pause_wanderer"),  vbox)
	_cb_pawn_down = _make_checkbox(Strings.t(&"ui.settings.pause_pawn_down"), vbox)
	_cb_modal     = _make_checkbox(Strings.t(&"ui.settings.pause_modal"),     vbox)

	_add_separator(vbox)

	# ── Audio section (stub — values saved; bus wiring Phase 18) ─────────────
	var audio_hdr := Label.new()
	audio_hdr.text = Strings.t(&"ui.settings.audio")
	vbox.add_child(audio_hdr)

	_sl_master  = _make_slider(Strings.t(&"ui.settings.master"),  vbox)
	_sl_music   = _make_slider(Strings.t(&"ui.settings.music"),   vbox)
	_sl_sfx     = _make_slider(Strings.t(&"ui.settings.sfx"),     vbox)
	_sl_ambient = _make_slider(Strings.t(&"ui.settings.ambient"), vbox)

	_add_separator(vbox)

	# ── Accessibility section (stub — values saved; UI wiring later) ──────────
	var access_hdr := Label.new()
	access_hdr.text = Strings.t(&"ui.settings.accessibility")
	vbox.add_child(access_hdr)

	_cb_large_text    = _make_checkbox(Strings.t(&"ui.settings.larger_text"),    vbox)
	_cb_reduce_motion = _make_checkbox(Strings.t(&"ui.settings.reduce_motion"),  vbox)

	_add_separator(vbox)

	# ── Save / Cancel row ─────────────────────────────────────────────────────
	var btn_row := HBoxContainer.new()
	btn_row.alignment = BoxContainer.ALIGNMENT_CENTER
	btn_row.add_theme_constant_override("separation", 16)
	vbox.add_child(btn_row)

	var save_btn := Button.new()
	save_btn.name = "SaveBtn"
	save_btn.text = Strings.t(&"ui.settings.save")
	save_btn.custom_minimum_size = Vector2(120, 48)
	save_btn.focus_mode = Control.FOCUS_NONE
	save_btn.pressed.connect(_on_save_pressed)
	btn_row.add_child(save_btn)

	var cancel_btn := Button.new()
	cancel_btn.name = "CancelBtn"
	cancel_btn.text = Strings.t(&"ui.settings.cancel")
	cancel_btn.custom_minimum_size = Vector2(120, 48)
	cancel_btn.focus_mode = Control.FOCUS_NONE
	cancel_btn.pressed.connect(_on_cancel_pressed)
	btn_row.add_child(cancel_btn)


func _make_checkbox(label_text: String, parent: VBoxContainer) -> CheckBox:
	var cb := CheckBox.new()
	cb.text = label_text
	cb.custom_minimum_size = Vector2(0, 48)
	cb.focus_mode = Control.FOCUS_NONE
	parent.add_child(cb)
	return cb


func _make_slider(label_text: String, parent: VBoxContainer) -> HSlider:
	var row := HBoxContainer.new()
	row.add_theme_constant_override("separation", 8)
	parent.add_child(row)

	var lbl := Label.new()
	lbl.text = label_text
	lbl.custom_minimum_size = Vector2(72, 0)
	row.add_child(lbl)

	var sl := HSlider.new()
	sl.min_value = 0.0
	sl.max_value = 1.0
	sl.step = 0.05
	sl.value = 1.0
	sl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	sl.custom_minimum_size = Vector2(0, 48)
	sl.focus_mode = Control.FOCUS_NONE
	row.add_child(sl)
	return sl


func _add_separator(parent: VBoxContainer) -> void:
	parent.add_child(HSeparator.new())


# ── state I/O ─────────────────────────────────────────────────────────────────

func _load_from_game_state() -> void:
	var s: Dictionary = GameState.settings
	_cb_threat.button_pressed    = bool(s.get("pause_on_threat",    true))
	_cb_wanderer.button_pressed  = bool(s.get("pause_on_wanderer",  true))
	_cb_pawn_down.button_pressed = bool(s.get("pause_on_pawn_down", true))
	_cb_modal.button_pressed     = bool(s.get("pause_on_modal",     true))

	_sl_master.value  = float(s.get("audio_master",  1.0))
	_sl_music.value   = float(s.get("audio_music",   1.0))
	_sl_sfx.value     = float(s.get("audio_sfx",     1.0))
	_sl_ambient.value = float(s.get("audio_ambient", 1.0))

	# Push initial slider values into the live audio buses + bind live updates.
	# Done here rather than _ready because _load_from_game_state fires after
	# GameState has restored settings from disk.
	if Audio != null:
		Audio.set_master_linear(_sl_master.value)
		Audio.set_music_linear(_sl_music.value)
		Audio.set_sfx_linear(_sl_sfx.value)
		if not _sl_master.value_changed.is_connected(Audio.set_master_linear):
			_sl_master.value_changed.connect(Audio.set_master_linear)
			_sl_music.value_changed.connect(Audio.set_music_linear)
			_sl_sfx.value_changed.connect(Audio.set_sfx_linear)
			# Ambient slider isn't wired to a bus yet (no ambient bus in Phase 18).
			# Phase 19 onboarding pass may add a third bus for nature SFX.

	_cb_large_text.button_pressed    = bool(s.get("accessibility_large_text",    false))
	_cb_reduce_motion.button_pressed = bool(s.get("accessibility_reduce_motion", false))


func _collect_to_dict() -> Dictionary:
	return {
		"pause_on_threat":    _cb_threat.button_pressed,
		"pause_on_wanderer":  _cb_wanderer.button_pressed,
		"pause_on_pawn_down": _cb_pawn_down.button_pressed,
		"pause_on_modal":     _cb_modal.button_pressed,
		"audio_master":       _sl_master.value,
		"audio_music":        _sl_music.value,
		"audio_sfx":          _sl_sfx.value,
		"audio_ambient":      _sl_ambient.value,
		"accessibility_large_text":    _cb_large_text.button_pressed,
		"accessibility_reduce_motion": _cb_reduce_motion.button_pressed,
	}


# ── interaction ───────────────────────────────────────────────────────────────

func _unhandled_input(event: InputEvent) -> void:
	# Comma — toggle settings menu.  Guard: don't open if already in a modal
	# (this IS the modal, so just toggle based on current visibility).
	if event.is_action_pressed("open_settings"):
		if _dim != null and _dim.visible:
			_set_visible(false)
			Audit.log("settings_menu", "hotkey close")
		else:
			open()
			Audit.log("settings_menu", "hotkey open")
		get_viewport().set_input_as_handled()
		return
	# Escape — close if open (discards edits, same as Cancel button).
	if event.is_action_pressed("cancel") and _dim != null and _dim.visible:
		Audit.log("settings_menu", "escape: cancelled")
		_set_visible(false)
		get_viewport().set_input_as_handled()
		return


func _on_save_pressed() -> void:
	GameState.apply_settings(_collect_to_dict())
	Audit.log("settings_menu", "settings saved")
	_set_visible(false)


func _on_cancel_pressed() -> void:
	Audit.log("settings_menu", "settings cancelled")
	_set_visible(false)


# ── visibility ────────────────────────────────────────────────────────────────

func _set_visible(v: bool) -> void:
	# Toggle CanvasLayer.visible so the dim Control (MOUSE_FILTER_STOP) does not
	# eat _unhandled_input mouse events for the world below when modal is hidden.
	visible = v
	if _dim != null:
		_dim.visible = v
	if _panel != null:
		_panel.visible = v
