class_name Thought extends RefCounted
## A single mood modifier entry, either state-driven (PERSISTENT) or
## one-shot event-driven (EVENT).
##
## PERSISTENT thoughts are added / removed by Pawn._refresh_persistent_thoughts
## each sim tick based on the pawn's current state (e.g. is_hungry()).
## They have no ticks_remaining — they exist for exactly as long as the
## triggering state is true.
##
## EVENT thoughts fire once on a game event (eating a meal, sleeping in a bed)
## and decay after ticks_remaining reaches zero. They stack up to max_stacks —
## for example, eating multiple meals in a row adds stacks to &"ate_meal"
## rather than duplicating entries.
##
## Save/load contract:
##   var t2 := Thought.from_dict(t.to_dict())
##   assert(t2.id == t.id and t2.stacks == t.stacks)
##
## docs/architecture.md "MoodSystem" + MAX_STACKS_PER_THOUGHT = 5 (locked).

enum Lifetime {
	PERSISTENT,  ## Present while a triggering state is true; Pawn manages add/remove.
	EVENT,       ## Fires once on a transition; decays after ticks_remaining ticks.
}

## Hard cap per thought type (docs/architecture.md "MoodSystem" — locked).
const MAX_STACKS_PER_THOUGHT: int = 5

## Unique identifier, e.g. &"hungry", &"well_rested". Used as the merge key.
var id: StringName = &""

## Human-readable label for Audit logs and future pawn-detail UI.
## Not i18n'd here; call Strings.t("thought." + id) for player-visible text.
var label: String = ""

## Mood delta per stack. Negative = bad, positive = good.
## Typical range -8..+8. Capped at max_stacks when computing mood.
var modifier: int = 0

## Whether this thought decays over time (EVENT) or tracks live state (PERSISTENT).
var lifetime: Lifetime = Lifetime.EVENT

## Per-thought stack ceiling. Usually MAX_STACKS_PER_THOUGHT; some thoughts are
## capped lower (e.g. hungry is max_stacks=1 because it's binary present/absent).
var max_stacks: int = MAX_STACKS_PER_THOUGHT

# ── Runtime state (owned by the Pawn after add_thought()) ────────────────────

## Number of times this thought is currently stacked (1..max_stacks).
var stacks: int = 1

## Remaining sim ticks before this EVENT thought expires. PERSISTENT ignores this.
var ticks_remaining: int = 0


# ── save / load ───────────────────────────────────────────────────────────────

func to_dict() -> Dictionary:
	return {
		"id": String(id),
		"label": label,
		"modifier": modifier,
		"lifetime": lifetime,
		"max_stacks": max_stacks,
		"stacks": stacks,
		"ticks_remaining": ticks_remaining,
	}


static func from_dict(d: Dictionary) -> Thought:
	var t := Thought.new()
	t.id = StringName(d.get("id", ""))
	t.label = str(d.get("label", ""))
	t.modifier = int(d.get("modifier", 0))
	t.lifetime = int(d.get("lifetime", Lifetime.EVENT)) as Lifetime
	t.max_stacks = int(d.get("max_stacks", MAX_STACKS_PER_THOUGHT))
	t.stacks = int(d.get("stacks", 1))
	t.ticks_remaining = int(d.get("ticks_remaining", 0))
	return t
