class_name EatProvider extends WorkProvider
## WorkProvider for the Eating work category. Slots into the 5-layer pawn AI
## (Decision → WorkProvider → Job + JobRunner) as layer 2.
##
## When a pawn is hungry and not already carrying an item, scans World.items for
## the nearest edible item and builds a 3-toil eat job: walk → pickup → eat.
##
## Food priority ladder (higher = preferred):
##   3  TYPE_MEAL       — cooked meal, most nutrition
##   2  TYPE_BREAD      — baked, mid-tier
##   1  TYPE_VEGETABLE  — raw veg, acceptable
##   0  TYPE_GRAIN      — raw grain, last resort (−5 mood "Ate raw food" in Phase 8)
##
## Priority 7 means hungry pawns prefer eating over hauling (3) and construction
## (6, for general builds), but does not override a player forced-job (Layer 2).
## When hunger drops below is_starving() the Phase 9 status interrupt will
## preempt current jobs — that hook is not yet wired; EatProvider handles
## voluntary eating only.
##
## Pawn is intentionally duck-typed (no class_name reference) to avoid the
## class_name registration-order trap documented in Phase 2.
##
## See docs/architecture.md "Pawn AI / job system" and
##     docs/design.md "Health & status effects".


func _init() -> void:
	category = &"eat"
	# 7 > construction (6) > hauling (3) > rest (0).
	# Hunger trumps routine work but player-forced jobs (Layer 2) still win.
	priority = 7


# ── WorkProvider override ─────────────────────────────────────────────────────

## Returns an eat Job for `pawn`, or null if the pawn is not hungry or there is
## no reachable food in the world.
##
## Selection logic:
##   Higher food-priority type always beats a closer lower-priority item.
##   Within the same priority tier, nearest item (Manhattan distance) wins.
##   Being-carried items are skipped — another pawn has claimed them.
func find_best_for(pawn) -> Job:
	if not pawn.is_hungry():
		return null
	# Don't interrupt an ongoing carry — the pawn should drop or deposit first.
	if pawn.carried_item != null:
		return null

	var best = null
	var best_dist: int = 999999
	var best_priority: int = -1

	for it in World.items:
		if it.being_carried:
			continue
		var fp: int = _food_priority(it.item_type)
		if fp < 0:
			continue
		# Reachability — same pattern as HaulingProvider. Skip food we can't
		# path to instead of returning a doomed walk job.
		if pawn.tile != it.tile and World.pathfinder.find_path(pawn.tile, it.tile).is_empty():
			continue
		var d: int = abs(it.tile.x - pawn.tile.x) + abs(it.tile.y - pawn.tile.y)
		# Higher food-priority tier beats distance; within same tier nearest wins.
		if fp > best_priority or (fp == best_priority and d < best_dist):
			best_priority = fp
			best_dist = d
			best = it

	if best == null:
		return null

	var j := Job.new()
	j.label = "Eat %s" % best.item_type
	j.toils.append(Toil.walk_to(best.tile))
	j.toils.append(Toil.pickup())
	j.toils.append(Toil.eat())
	return j


# ── private helpers ───────────────────────────────────────────────────────────

## Returns the food desirability for `item_type`, or -1 if not edible.
## Pawns prefer cooked food; raw grain is a reluctant last resort.
func _food_priority(item_type: StringName) -> int:
	match item_type:
		Item.TYPE_MEAL:      return 3
		Item.TYPE_BREAD:     return 2
		Item.TYPE_VEGETABLE: return 1
		Item.TYPE_GRAIN:     return 0
		_:                   return -1  # not edible
