class_name PawnDetailPanel extends CanvasLayer
## Phase 17 — Right-side bottom-sheet pawn inspector.
##
## Layer 18: above banner (15) and world, below modal (20) and load_menu (25).
## Opens when EventBus.pawn_selected fires; closes on pawn_deselected or pawn_died.
## Repopulates every 5 sim ticks while open so vitals stay live.
##
## Touch targets: all interactive controls are at least 48×48 px.
## Background elements use MOUSE_FILTER_IGNORE so world taps pass through,
## allowing the player to tap a different pawn to swap panels without closing first.

const PANEL_WIDTH: int = 360
const REFRESH_TICKS: int = 5   # repopulate every N sim ticks

# ── internal state ────────────────────────────────────────────────────────────
var _pawn = null            # currently displayed Pawn (may become null on death)
var _tick_counter: int = 0  # counts sim ticks since last repopulate

# ── node refs (built in _build_ui) ───────────────────────────────────────────
var _panel: PanelContainer = null
var _pawn_name_label: Label = null
var _portrait_rect: ColorRect = null
var _close_btn: Button = null

var _hp_bar: ProgressBar = null
var _hunger_bar: ProgressBar = null
var _sleep_bar: ProgressBar = null

var _sulk_badge: Label = null
var _job_label: Label = null
var _mood_label: Label = null

var _thoughts_vbox: VBoxContainer = null
var _statuses_vbox: VBoxContainer = null
var _skills_vbox: VBoxContainer = null
var _priorities_label: Label = null


func _ready() -> void:
	layer = 18

	_build_ui()
	_set_visible(false)

	EventBus.pawn_selected.connect(_on_pawn_selected)
	EventBus.pawn_deselected.connect(_on_pawn_deselected)
	if EventBus.has_signal("pawn_died"):
		EventBus.pawn_died.connect(_on_pawn_died)
	EventBus.sim_tick.connect(_on_sim_tick)

	Audit.log("pawn_detail_panel", "PawnDetailPanel ready (layer %d)" % layer)


func _exit_tree() -> void:
	if EventBus.pawn_selected.is_connected(_on_pawn_selected):
		EventBus.pawn_selected.disconnect(_on_pawn_selected)
	if EventBus.pawn_deselected.is_connected(_on_pawn_deselected):
		EventBus.pawn_deselected.disconnect(_on_pawn_deselected)
	if EventBus.has_signal("pawn_died") and EventBus.pawn_died.is_connected(_on_pawn_died):
		EventBus.pawn_died.disconnect(_on_pawn_died)
	if EventBus.sim_tick.is_connected(_on_sim_tick):
		EventBus.sim_tick.disconnect(_on_sim_tick)


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

func _build_ui() -> void:
	# Right-side sheet — anchored to the right edge, full height.
	_panel = PanelContainer.new()
	_panel.name = "PawnDetailSheet"
	# Anchor: right strip.
	_panel.anchor_left   = 1.0
	_panel.anchor_right  = 1.0
	_panel.anchor_top    = 0.0
	_panel.anchor_bottom = 1.0
	_panel.offset_left   = -PANEL_WIDTH
	_panel.offset_right  = 0.0
	_panel.offset_top    = 0.0
	_panel.offset_bottom = 0.0
	# Pass-through for the background so world taps reach the Selection handler.
	_panel.mouse_filter = Control.MOUSE_FILTER_PASS
	add_child(_panel)

	# Scrollable inner container so content survives small screens.
	var scroll := ScrollContainer.new()
	scroll.name = "Scroll"
	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.name = "Content"
	vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	vbox.add_theme_constant_override("separation", 6)
	scroll.add_child(vbox)

	# ── Header ────────────────────────────────────────────────────────────────
	var header := HBoxContainer.new()
	header.name = "Header"
	header.add_theme_constant_override("separation", 8)
	vbox.add_child(header)

	_portrait_rect = ColorRect.new()
	_portrait_rect.name = "Portrait"
	_portrait_rect.custom_minimum_size = Vector2(32, 32)
	_portrait_rect.color = Color.WHITE
	_portrait_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
	header.add_child(_portrait_rect)

	_pawn_name_label = Label.new()
	_pawn_name_label.name = "PawnName"
	_pawn_name_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	_pawn_name_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
	_pawn_name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
	header.add_child(_pawn_name_label)

	_close_btn = Button.new()
	_close_btn.name = "CloseBtn"
	_close_btn.text = Strings.t(&"ui.detail.close")
	_close_btn.custom_minimum_size = Vector2(48, 48)
	_close_btn.focus_mode = Control.FOCUS_NONE
	_close_btn.pressed.connect(_on_close_pressed)
	header.add_child(_close_btn)

	_add_separator(vbox)

	# ── Vitals ────────────────────────────────────────────────────────────────
	var vitals_vbox := VBoxContainer.new()
	vitals_vbox.name = "Vitals"
	vitals_vbox.add_theme_constant_override("separation", 4)
	vitals_vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(vitals_vbox)

	_hp_bar = _make_stat_bar(Strings.t(&"ui.detail.hp"), vitals_vbox)
	_hunger_bar = _make_stat_bar(Strings.t(&"ui.detail.hunger"), vitals_vbox)
	_sleep_bar = _make_stat_bar(Strings.t(&"ui.detail.sleep"), vitals_vbox)

	_add_separator(vbox)

	# ── Status row (sulk badge + job) ─────────────────────────────────────────
	var status_row := HBoxContainer.new()
	status_row.name = "StatusRow"
	status_row.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(status_row)

	_sulk_badge = Label.new()
	_sulk_badge.name = "SulkBadge"
	_sulk_badge.text = Strings.t(&"ui.detail.sulking")
	_sulk_badge.modulate = Color(1.0, 0.25, 0.25)
	_sulk_badge.visible = false
	_sulk_badge.mouse_filter = Control.MOUSE_FILTER_IGNORE
	status_row.add_child(_sulk_badge)

	_job_label = Label.new()
	_job_label.name = "JobLabel"
	_job_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	_job_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
	_job_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
	status_row.add_child(_job_label)

	_add_separator(vbox)

	# ── Mood + thoughts ───────────────────────────────────────────────────────
	_mood_label = Label.new()
	_mood_label.name = "MoodLabel"
	_mood_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(_mood_label)

	_thoughts_vbox = VBoxContainer.new()
	_thoughts_vbox.name = "Thoughts"
	_thoughts_vbox.add_theme_constant_override("separation", 2)
	_thoughts_vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(_thoughts_vbox)

	_add_separator(vbox)

	# ── Statuses ──────────────────────────────────────────────────────────────
	var stat_header := Label.new()
	stat_header.text = Strings.t(&"ui.detail.statuses")
	stat_header.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(stat_header)

	_statuses_vbox = VBoxContainer.new()
	_statuses_vbox.name = "Statuses"
	_statuses_vbox.add_theme_constant_override("separation", 2)
	_statuses_vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(_statuses_vbox)

	_add_separator(vbox)

	# ── Skills ────────────────────────────────────────────────────────────────
	var skills_header := Label.new()
	skills_header.text = Strings.t(&"ui.detail.skills")
	skills_header.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(skills_header)

	_skills_vbox = VBoxContainer.new()
	_skills_vbox.name = "Skills"
	_skills_vbox.add_theme_constant_override("separation", 2)
	_skills_vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(_skills_vbox)

	_add_separator(vbox)

	# ── Work priorities (read-only; Agent C adds the tap-to-cycle matrix) ─────
	var prio_header := Label.new()
	prio_header.text = Strings.t(&"ui.detail.priorities")
	prio_header.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(prio_header)

	_priorities_label = Label.new()
	_priorities_label.name = "Priorities"
	_priorities_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
	_priorities_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(_priorities_label)


## Build a labelled ProgressBar row inside parent_vbox.  Returns the ProgressBar.
func _make_stat_bar(label_text: String, parent: VBoxContainer) -> ProgressBar:
	var row := HBoxContainer.new()
	row.add_theme_constant_override("separation", 6)
	row.mouse_filter = Control.MOUSE_FILTER_IGNORE
	parent.add_child(row)

	var lbl := Label.new()
	lbl.text = label_text
	lbl.custom_minimum_size = Vector2(56, 0)
	lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
	row.add_child(lbl)

	var bar := ProgressBar.new()
	bar.min_value = 0.0
	bar.max_value = 100.0
	bar.value = 100.0
	bar.show_percentage = false
	bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	bar.custom_minimum_size = Vector2(0, 20)
	bar.mouse_filter = Control.MOUSE_FILTER_IGNORE
	row.add_child(bar)
	return bar


func _add_separator(parent: VBoxContainer) -> void:
	var sep := HSeparator.new()
	sep.mouse_filter = Control.MOUSE_FILTER_IGNORE
	parent.add_child(sep)


# ── event handlers ────────────────────────────────────────────────────────────

func _on_pawn_selected(pawn) -> void:
	_pawn = pawn
	_tick_counter = 0
	_populate()
	_set_visible(true)
	Audit.log("pawn_detail_panel", "opened for %s" % pawn.pawn_name)


func _on_pawn_deselected() -> void:
	_pawn = null
	_set_visible(false)
	Audit.log("pawn_detail_panel", "closed (deselected)")


func _on_pawn_died(pawn, _cause: StringName) -> void:
	if _pawn == pawn or _pawn == null:
		_pawn = null
		_set_visible(false)
		Audit.log("pawn_detail_panel", "closed (pawn died)")


func _on_close_pressed() -> void:
	# Mirror a deselect — clear selection state so the world is consistent.
	_pawn = null
	_set_visible(false)
	EventBus.pawn_deselected.emit()
	Audit.log("pawn_detail_panel", "closed (X button)")


func _on_sim_tick(_tick_number: int) -> void:
	if _pawn == null or not _panel.visible:
		return
	# Guard against a pawn that was freed without emitting pawn_died.
	if not is_instance_valid(_pawn):
		_pawn = null
		_set_visible(false)
		return
	_tick_counter += 1
	if _tick_counter >= REFRESH_TICKS:
		_tick_counter = 0
		_populate()


# ── population ────────────────────────────────────────────────────────────────

func _populate() -> void:
	if _pawn == null or not is_instance_valid(_pawn):
		return

	# Header.
	_pawn_name_label.text = _pawn.pawn_name
	_portrait_rect.color = _pawn.portrait_color

	# Vitals.
	_hp_bar.value = _pawn.hp
	_color_threshold_bar(_hp_bar, _pawn.hp, 30.0, 60.0)

	_hunger_bar.value = _pawn.hunger
	_color_threshold_bar(_hunger_bar, _pawn.hunger, 30.0, 60.0)

	_sleep_bar.value = _pawn.sleep
	_color_threshold_bar(_sleep_bar, _pawn.sleep, 30.0, 60.0)

	# Status row.
	_sulk_badge.visible = _pawn.sulking

	var job_text: String = Strings.t(&"ui.detail.idle")
	if _pawn.job_runner != null and _pawn.job_runner.has_method("current_job_label"):
		var lbl: String = _pawn.job_runner.current_job_label()
		if lbl != "":
			job_text = lbl
	elif _pawn.job_runner != null and _pawn.job_runner.get("current_job") != null:
		var cj = _pawn.job_runner.get("current_job")
		if cj != null and cj.get("label") != null and cj.label != "":
			job_text = cj.label
	_job_label.text = job_text

	# Mood.
	var mood_val: float = _pawn.mood
	_mood_label.text = "%s: %.0f" % [Strings.t(&"ui.detail.mood"), mood_val]
	if mood_val < 30.0:
		_mood_label.modulate = Color(1.0, 0.3, 0.3)
	elif mood_val < 60.0:
		_mood_label.modulate = Color(1.0, 0.85, 0.3)
	else:
		_mood_label.modulate = Color(0.4, 1.0, 0.4)

	# Thoughts — top 5 by |modifier × stacks| desc.
	_clear_children(_thoughts_vbox)
	var sorted_thoughts: Array = _pawn.thoughts.duplicate()
	sorted_thoughts.sort_custom(func(a, b) -> bool:
		return abs(a.modifier * a.stacks) > abs(b.modifier * b.stacks)
	)
	var count: int = mini(5, sorted_thoughts.size())
	for i in range(count):
		var t = sorted_thoughts[i]
		var delta: int = t.modifier * mini(t.stacks, t.max_stacks)
		var sign_str: String = "+" if delta >= 0 else ""
		var row := Label.new()
		row.text = "  %s  %s%d" % [t.label, sign_str, delta]
		row.mouse_filter = Control.MOUSE_FILTER_IGNORE
		_thoughts_vbox.add_child(row)

	# Statuses.
	_clear_children(_statuses_vbox)
	for s in _pawn.statuses:
		var row := Label.new()
		row.text = "  %s  %s" % [s.label, Strings.t(&"ui.detail.sev").format({"s": s.severity, "m": s.max_severity})]
		row.mouse_filter = Control.MOUSE_FILTER_IGNORE
		_statuses_vbox.add_child(row)

	# Skills.
	_clear_children(_skills_vbox)
	var skill_key_map: Array = [
		[&"manual_labor", &"ui.detail.skill.manual_labor"],
		[&"crafting",     &"ui.detail.skill.crafting"],
		[&"cooking",      &"ui.detail.skill.cooking"],
		[&"medicine",     &"ui.detail.skill.medicine"],
		[&"combat",       &"ui.detail.skill.combat"],
	]
	for pair in skill_key_map:
		var skill_id: StringName = pair[0]
		var skill_lbl: StringName = pair[1]
		if not _pawn.skills.has(skill_id):
			continue
		var row := HBoxContainer.new()
		row.add_theme_constant_override("separation", 8)
		row.mouse_filter = Control.MOUSE_FILTER_IGNORE
		_skills_vbox.add_child(row)

		var name_lbl := Label.new()
		name_lbl.text = Strings.t(skill_lbl)
		name_lbl.custom_minimum_size = Vector2(72, 0)
		name_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
		row.add_child(name_lbl)

		var val_lbl := Label.new()
		val_lbl.text = str(_pawn.skills[skill_id])
		val_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
		row.add_child(val_lbl)

	# Work priorities — comma-separated single line (Agent C adds interactive matrix).
	var prio_parts: Array[String] = []
	for cat in _pawn.work_priorities:
		prio_parts.append("%s: %d" % [String(cat), _pawn.work_priorities[cat]])
	_priorities_label.text = ", ".join(prio_parts)


## Color a progress bar by value thresholds: red below low, yellow below high, green above.
func _color_threshold_bar(bar: ProgressBar, value: float, low: float, high: float) -> void:
	if value < low:
		bar.modulate = Color(1.0, 0.25, 0.25)
	elif value < high:
		bar.modulate = Color(1.0, 0.85, 0.25)
	else:
		bar.modulate = Color(0.3, 1.0, 0.3)


func _clear_children(node: Node) -> void:
	for child in node.get_children():
		child.queue_free()


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

func _set_visible(v: bool) -> void:
	if _panel != null:
		_panel.visible = v
