class_name StockpilePanel extends CanvasLayer
## Phase 17 — Right-side stockpile filter + priority editor.
##
## Layer 18: shares the slot with WorkbenchPanel and PawnDetailPanel — only one
## is visible at a time (mutual exclusion via Selection).
## Opens when EventBus.stockpile_selected fires; closes on stockpile_deselected.
##
## Filter model:
##   accepted_types.is_empty()  →  wildcard "accept all" mode (default).
##   As soon as the player checks any chip, the zone switches to explicit-list
##   mode.  "Select all" returns to wildcard by clearing the array.
##
## Rebuild policy:
##   _populate_chips / _update_priority_row are called only on open or on an
##   explicit player action.  No per-tick polling needed.
##
## Touch targets: all interactive controls are at least 48×48 px.

const PANEL_WIDTH: int = 360
const LAYER: int = 18

# ── internal state ────────────────────────────────────────────────────────────
var current_zone = null  # StockpileZone (StorageDestination subclass)

# ── node refs (built in _build_ui) ───────────────────────────────────────────
var _panel: PanelContainer = null
var _header_label: Label = null
var _close_btn: Button = null
var _priority_btns: Array[Button] = []
var _chips_grid: GridContainer = null
var _all_label: Label = null


func _ready() -> void:
	layer = LAYER

	_build_ui()
	_set_visible(false)

	EventBus.stockpile_selected.connect(_on_stockpile_selected)
	EventBus.stockpile_deselected.connect(_on_stockpile_deselected)
	EventBus.pawn_selected.connect(_on_other_selected)
	EventBus.workbench_selected.connect(_on_other_selected)

	Audit.log("stockpile_panel", "StockpilePanel ready (layer %d)" % layer)


func _exit_tree() -> void:
	if EventBus.stockpile_selected.is_connected(_on_stockpile_selected):
		EventBus.stockpile_selected.disconnect(_on_stockpile_selected)
	if EventBus.stockpile_deselected.is_connected(_on_stockpile_deselected):
		EventBus.stockpile_deselected.disconnect(_on_stockpile_deselected)
	if EventBus.pawn_selected.is_connected(_on_other_selected):
		EventBus.pawn_selected.disconnect(_on_other_selected)
	if EventBus.workbench_selected.is_connected(_on_other_selected):
		EventBus.workbench_selected.disconnect(_on_other_selected)


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

func _build_ui() -> void:
	# Right-side sheet anchored to the right edge, full height.
	_panel = PanelContainer.new()
	_panel.name = "StockpileSheet"
	_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
	_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)

	_header_label = Label.new()
	_header_label.name = "HeaderLabel"
	_header_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	_header_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
	_header_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
	header.add_child(_header_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)

	# ── Priority row ──────────────────────────────────────────────────────────
	var prio_lbl := Label.new()
	prio_lbl.text = Strings.t(&"ui.stockpile.priority")
	prio_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(prio_lbl)

	var prio_row := HBoxContainer.new()
	prio_row.name = "PriorityRow"
	prio_row.add_theme_constant_override("separation", 4)
	vbox.add_child(prio_row)

	# Buttons ordered CRITICAL → HIGH → NORMAL → LOW → OFF for display.
	# StorageDestination.Priority has CRITICAL=0, HIGH=1, NORMAL=2, LOW=3, OFF=4.
	var prio_labels: Array[StringName] = [
		&"ui.stockpile.prio.critical",
		&"ui.stockpile.prio.high",
		&"ui.stockpile.prio.normal",
		&"ui.stockpile.prio.low",
		&"ui.stockpile.prio.off",
	]
	_priority_btns.clear()
	for i in prio_labels.size():
		var btn := Button.new()
		btn.text = Strings.t(prio_labels[i])
		btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
		btn.custom_minimum_size = Vector2(0, 40)
		btn.focus_mode = Control.FOCUS_NONE
		btn.toggle_mode = false
		var captured_i: int = i
		btn.pressed.connect(func() -> void: _on_priority_pressed(captured_i))
		prio_row.add_child(btn)
		_priority_btns.append(btn)

	_add_separator(vbox)

	# ── Filter chip section ────────────────────────────────────────────────────
	var filter_hdr := HBoxContainer.new()
	filter_hdr.name = "FilterHeader"
	filter_hdr.add_theme_constant_override("separation", 8)
	vbox.add_child(filter_hdr)

	var accepts_lbl := Label.new()
	accepts_lbl.text = Strings.t(&"ui.stockpile.accepts")
	accepts_lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	accepts_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
	filter_hdr.add_child(accepts_lbl)

	_all_label = Label.new()
	_all_label.name = "AllLabel"
	_all_label.text = Strings.t(&"ui.stockpile.accepts_all_hint")
	_all_label.modulate = Color(0.7, 0.7, 0.7)
	_all_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
	filter_hdr.add_child(_all_label)

	# "Select all" / "Clear all" quick-action row.
	var quick_row := HBoxContainer.new()
	quick_row.name = "QuickRow"
	quick_row.add_theme_constant_override("separation", 8)
	vbox.add_child(quick_row)

	var select_all_btn := Button.new()
	select_all_btn.name = "SelectAllBtn"
	select_all_btn.text = Strings.t(&"ui.stockpile.select_all")
	select_all_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	select_all_btn.custom_minimum_size = Vector2(0, 40)
	select_all_btn.focus_mode = Control.FOCUS_NONE
	select_all_btn.pressed.connect(_on_select_all_pressed)
	quick_row.add_child(select_all_btn)

	var clear_all_btn := Button.new()
	clear_all_btn.name = "ClearAllBtn"
	clear_all_btn.text = Strings.t(&"ui.stockpile.clear_all")
	clear_all_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	clear_all_btn.custom_minimum_size = Vector2(0, 40)
	clear_all_btn.focus_mode = Control.FOCUS_NONE
	clear_all_btn.pressed.connect(_on_clear_all_pressed)
	quick_row.add_child(clear_all_btn)

	# 4-column chip grid.
	_chips_grid = GridContainer.new()
	_chips_grid.name = "ChipsGrid"
	_chips_grid.columns = 4
	_chips_grid.add_theme_constant_override("h_separation", 4)
	_chips_grid.add_theme_constant_override("v_separation", 4)
	_chips_grid.mouse_filter = Control.MOUSE_FILTER_IGNORE
	vbox.add_child(_chips_grid)


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


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


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

func _on_stockpile_selected(zone) -> void:
	current_zone = zone
	_refresh_header()
	_update_priority_row()
	_populate_chips()
	_set_visible(true)
	Audit.log("stockpile_panel", "opened for zone at %s" % str(zone.position))


func _on_stockpile_deselected() -> void:
	current_zone = null
	_set_visible(false)
	Audit.log("stockpile_panel", "closed (deselected)")


func _on_other_selected(_ignored = null) -> void:
	# Mutual exclusion: any other selection type hides this panel.
	if current_zone == null:
		return
	current_zone = null
	_set_visible(false)
	Audit.log("stockpile_panel", "closed (other panel selected)")


func _on_close_pressed() -> void:
	current_zone = null
	_set_visible(false)
	EventBus.stockpile_deselected.emit()
	Audit.log("stockpile_panel", "closed (X button)")


func _on_priority_pressed(prio_index: int) -> void:
	if current_zone == null:
		return
	current_zone.priority = prio_index as StorageDestination.Priority
	_update_priority_row()
	EventBus.stockpile_layout_changed.emit()
	Audit.log("stockpile_panel", "priority → %d" % prio_index)


func _on_select_all_pressed() -> void:
	if current_zone == null:
		return
	# Empty array = wildcard — accept all. call_deferred to avoid freeing chips
	# while this button's pressed signal is still emitting.
	current_zone.accepted_types.clear()
	call_deferred("_populate_chips")
	EventBus.stockpile_layout_changed.emit()
	Audit.log("stockpile_panel", "select_all → wildcard mode")


func _on_clear_all_pressed() -> void:
	if current_zone == null:
		return
	# Start explicit-list mode but with nothing checked.
	# We set accepted_types to all types so *nothing* is excluded yet, then
	# clear to empty which means… wildcard. To mean "accept nothing" we need
	# Priority.OFF instead. So: clear all chips means set priority to OFF and
	# leave accepted_types alone (wildcard doesn't matter when OFF).
	# The idiomatic result the player expects from "Clear all" on a filter grid
	# is that the zone accepts nothing — set priority to OFF.
	current_zone.priority = StorageDestination.Priority.OFF
	_update_priority_row()
	call_deferred("_populate_chips")
	EventBus.stockpile_layout_changed.emit()
	Audit.log("stockpile_panel", "clear_all → priority OFF")


# ── refresh helpers ────────────────────────────────────────────────────────────

func _refresh_header() -> void:
	if current_zone == null:
		return
	var tile_count: int = 0
	if current_zone.has_method("tile_count"):
		tile_count = current_zone.tile_count()
	elif current_zone.get("tiles") != null:
		tile_count = current_zone.tiles.size()
	_header_label.text = "%s (%d)" % [Strings.t(&"ui.stockpile.title"), tile_count]


func _update_priority_row() -> void:
	if current_zone == null:
		return
	var active: int = int(current_zone.priority)
	# CRITICAL=0 HIGH=1 NORMAL=2 LOW=3 OFF=4 — matches button array order.
	var active_tint := Color(0.90, 0.65, 0.20)
	var inactive_tint := Color(1.0, 1.0, 1.0)
	for i in _priority_btns.size():
		_priority_btns[i].modulate = active_tint if i == active else inactive_tint


func _populate_chips() -> void:
	if current_zone == null:
		return

	_clear_children(_chips_grid)

	var is_wildcard: bool = current_zone.accepted_types.is_empty()
	_all_label.visible = is_wildcard

	for type in Item.ALL_TYPES:
		var chip := _make_chip(type, is_wildcard or (type in current_zone.accepted_types))
		_chips_grid.add_child(chip)


## Build a single filter chip Button for one item type.
## checked=true renders the chip as "included" (white/bright), false as dimmed.
func _make_chip(type: StringName, checked: bool) -> Button:
	# Label: try "item.<type>" key; Strings.t() returns the key string itself
	# when missing (and emits a push_warning), so we detect that and fall back
	# to a capitalized form without the "item." prefix.
	var label_key: StringName = StringName("item." + String(type))
	var looked_up: String = Strings.t(label_key)
	var label_text: String
	if looked_up != String(label_key):
		label_text = looked_up
	else:
		label_text = String(type).capitalize()

	var btn := Button.new()
	btn.text = label_text
	btn.custom_minimum_size = Vector2(0, 40)
	btn.focus_mode = Control.FOCUS_NONE
	btn.toggle_mode = false
	btn.modulate = Color(1.0, 1.0, 1.0) if checked else Color(0.5, 0.5, 0.5, 0.8)

	btn.pressed.connect(func() -> void: _on_chip_pressed(type))
	return btn


func _on_chip_pressed(type: StringName) -> void:
	if current_zone == null:
		return

	var is_wildcard: bool = current_zone.accepted_types.is_empty()

	if is_wildcard:
		# Wildcard → explicit: start with all types checked, then remove this one.
		var explicit: Array[StringName] = []
		for t in Item.ALL_TYPES:
			if t != type:
				explicit.append(t)
		current_zone.accepted_types = explicit
	else:
		# Toggle: add if absent, remove if present.
		if type in current_zone.accepted_types:
			current_zone.accepted_types.erase(type)
			# If we just unchecked the last one, go back to wildcard.
			if current_zone.accepted_types.is_empty():
				pass  # empty = wildcard already
		else:
			current_zone.accepted_types.append(type)

	# Defer the rebuild — don't free this chip while its pressed signal emits.
	call_deferred("_populate_chips")
	EventBus.stockpile_layout_changed.emit()
	Audit.log("stockpile_panel", "chip toggled: %s → accepted_types size=%d" % [
		type, current_zone.accepted_types.size()
	])


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

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