class_name WorkPriorityMatrix extends CanvasLayer
## Phase 17 — Per-pawn work-priority matrix bottom-sheet.
##
## Opened via TopBar "Work" button (injected by main.gd).
## Displays one row per pawn × one column per player-configurable work category.
## Tapping a cell cycles the priority: 1 (Critical) → 2 → 3 → 4 (Low) → 0 (OFF) → 1.
##
## Priority semantics (mirrors Pawn.work_priorities and Decision layer 4):
##   0 = OFF  (gray)   — provider never picks this pawn for this category.
##   1 = Critical (red)
##   2 = High (orange)
##   3 = Normal (yellow) — default
##   4 = Low (blue)
##
## Category order matches the locked list in pawn.gd / Decision layer 4.
## &"doctor" is included — players can opt a pawn out of doctor duty even
## though the needs-driven healing path bypasses the filter at Layer 3.
##
## Save / load: NOT session-persistent for MVP — the matrix re-reads
## pawn.work_priorities live from the Pawn nodes every open, so any save that
## round-trips pawn.work_priorities (via pawn.to_dict) will reflect correctly.

const CATEGORIES: Array[StringName] = [
	&"construction", &"chop", &"plant", &"mine", &"crafting", &"haul", &"clean", &"doctor"
]
const CATEGORY_LABELS: Dictionary = {
	&"construction": "Build",
	&"chop":         "Chop",
	&"plant":        "Plant",
	&"mine":         "Mine",
	&"crafting":     "Craft",
	&"haul":         "Haul",
	&"clean":        "Clean",
	&"doctor":       "Doctor",
}

## Color per priority level for the cell buttons.
const PRIORITY_COLORS: Dictionary = {
	0: Color(0.45, 0.45, 0.45, 1.0),   # OFF — gray
	1: Color(0.85, 0.18, 0.18, 1.0),   # Critical — red
	2: Color(0.90, 0.55, 0.10, 1.0),   # High — orange
	3: Color(0.85, 0.80, 0.10, 1.0),   # Normal — yellow
	4: Color(0.20, 0.40, 0.85, 1.0),   # Low — blue
}
const PRIORITY_DISPLAY: Dictionary = {
	0: "X",
	1: "1",
	2: "2",
	3: "3",
	4: "4",
}

## Panel height in pixels.
const PANEL_HEIGHT: float = 600.0
## Row height for each pawn row.
const ROW_H: float = 48.0
## Column width for category cells.
const COL_W: float = 64.0
## Width of the pawn-name column.
const NAME_COL_W: float = 100.0

var _root: Control = null
var _scroll: ScrollContainer = null
var _grid: GridContainer = null
## Flat list of [pawn, category, button] triples so we can refresh on open.
var _cells: Array = []


func _ready() -> void:
	layer = 17
	_build_ui()
	# Hide the whole CanvasLayer when closed so the Backdrop ColorRect (which
	# has MOUSE_FILTER_STOP for click-outside-to-close) does not eat mouse
	# events in `_unhandled_input` for the world below. Toggling _root.visible
	# alone leaves the Backdrop alive and intercepting clicks.
	visible = false
	_root.visible = false
	Audit.log("work_priority_ui", "WorkPriorityMatrix ready")


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

## Open the matrix panel and rebuild the grid from current pawn state.
func open() -> void:
	_rebuild_grid()
	visible = true
	_root.visible = true
	Audit.log("work_priority_ui", "opened (pawns=%d)" % World.pawns.size())


## Close the panel.
func close() -> void:
	visible = false
	_root.visible = false
	Audit.log("work_priority_ui", "closed")


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

func _build_ui() -> void:
	# Full-screen semi-transparent backdrop.
	var backdrop := ColorRect.new()
	backdrop.name = "Backdrop"
	backdrop.set_anchors_preset(Control.PRESET_FULL_RECT)
	backdrop.color = Color(0.0, 0.0, 0.0, 0.45)
	backdrop.mouse_filter = Control.MOUSE_FILTER_STOP
	backdrop.gui_input.connect(_on_backdrop_input)
	add_child(backdrop)

	# Bottom-sheet panel anchored to the bottom of the screen.
	_root = Control.new()
	_root.name = "MatrixPanel"
	_root.set_anchors_preset(Control.PRESET_BOTTOM_WIDE)
	_root.custom_minimum_size = Vector2(0.0, PANEL_HEIGHT)
	_root.offset_top = -PANEL_HEIGHT
	_root.offset_bottom = 0.0
	_root.mouse_filter = Control.MOUSE_FILTER_STOP
	backdrop.add_child(_root)

	var bg := PanelContainer.new()
	bg.name = "BG"
	bg.set_anchors_preset(Control.PRESET_FULL_RECT)
	_root.add_child(bg)

	var vbox := VBoxContainer.new()
	vbox.add_theme_constant_override("separation", 4)
	bg.add_child(vbox)

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

	var title := Label.new()
	title.text = "Work Priorities"
	title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	header.add_child(title)

	var close_btn := Button.new()
	close_btn.text = "Close"
	close_btn.focus_mode = Control.FOCUS_NONE
	close_btn.custom_minimum_size = Vector2(72.0, 36.0)
	close_btn.pressed.connect(close)
	header.add_child(close_btn)

	# Scroll area for the grid (handles many pawns).
	_scroll = ScrollContainer.new()
	_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
	vbox.add_child(_scroll)

	# GridContainer: 1 + CATEGORIES.size() columns.
	_grid = GridContainer.new()
	_grid.columns = 1 + CATEGORIES.size()
	_grid.add_theme_constant_override("h_separation", 4)
	_grid.add_theme_constant_override("v_separation", 4)
	_scroll.add_child(_grid)


# ── grid rebuild ──────────────────────────────────────────────────────────────

func _rebuild_grid() -> void:
	# Clear existing children and cell tracking.
	for child in _grid.get_children():
		child.queue_free()
	_cells.clear()

	# Header row: "Pawn" + one label per category.
	_add_header_cell("Pawn")
	for cat in CATEGORIES:
		_add_header_cell(CATEGORY_LABELS.get(cat, String(cat)))

	# One data row per pawn in World.pawns.
	for pawn in World.pawns:
		# Pawn name cell.
		var name_lbl := Label.new()
		name_lbl.text = pawn.pawn_name
		name_lbl.custom_minimum_size = Vector2(NAME_COL_W, ROW_H)
		name_lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
		name_lbl.clip_text = true
		_grid.add_child(name_lbl)

		# One button per category.
		for cat in CATEGORIES:
			var lvl: int = int(pawn.work_priorities.get(cat, 3))
			var btn := Button.new()
			btn.focus_mode = Control.FOCUS_NONE
			btn.custom_minimum_size = Vector2(COL_W, ROW_H)
			_apply_cell_style(btn, lvl)
			# Capture pawn + category in the closure.
			var p = pawn
			var c: StringName = cat
			btn.pressed.connect(func() -> void: _on_cell_pressed(p, c, btn))
			_grid.add_child(btn)
			_cells.append([pawn, cat, btn])


func _add_header_cell(text: String) -> void:
	var lbl := Label.new()
	lbl.text = text
	lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
	lbl.custom_minimum_size = Vector2(COL_W, 32.0)
	lbl.add_theme_font_size_override("font_size", 11)
	_grid.add_child(lbl)


func _apply_cell_style(btn: Button, lvl: int) -> void:
	btn.text = PRIORITY_DISPLAY.get(lvl, "?")
	btn.modulate = PRIORITY_COLORS.get(lvl, Color.WHITE)


# ── cell interaction ──────────────────────────────────────────────────────────

func _on_cell_pressed(pawn, category: StringName, btn: Button) -> void:
	# Cycle: 1 → 2 → 3 → 4 → 0 → 1
	var current: int = int(pawn.work_priorities.get(category, 3))
	var next: int
	match current:
		0: next = 1
		1: next = 2
		2: next = 3
		3: next = 4
		4: next = 0
		_: next = 3

	pawn.work_priorities[category] = next
	_apply_cell_style(btn, next)

	EventBus.pawn_priority_changed.emit(pawn, category, next)
	Audit.log("work_priority_ui", "%s: %s → %d" % [pawn.pawn_name, String(category), next])


func _unhandled_input(event: InputEvent) -> void:
	# P — toggle the work priority matrix.
	if event.is_action_pressed("open_priorities"):
		if _root != null and _root.visible:
			close()
		else:
			open()
		get_viewport().set_input_as_handled()
		return
	# Escape — close if open.
	if event.is_action_pressed("cancel") and _root != null and _root.visible:
		close()
		get_viewport().set_input_as_handled()
		return


func _on_backdrop_input(event: InputEvent) -> void:
	# Tap on the backdrop (outside the panel) closes the matrix.
	if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
		close()
	elif event is InputEventScreenTouch and event.pressed:
		close()
