class_name CanonLog
extends RefCounted
## The §11 canon log: compact structured state code maintains and injects into
## every AI call. This container owns the container-level invariants; leaf rows
## own theirs. AI prose never writes here directly — mutators are called by code.

const SCHEMA_VERSION := 1
const MAX_RECENT_EVENTS := 5

var schema_version: int = SCHEMA_VERSION
var player: LogPlayer
var location: LogLocation
var party: Array[PartyMember] = []
var recent_events: Array[String] = []
var established_facts: Array[String] = []
var active_quests: Array[Quest] = []
var humiliations: Array[Humiliation] = []


func _init() -> void:
	player = LogPlayer.new()
	location = LogLocation.new()


# ── maintenance mutators (charter §11 maintenance table) ─────────────────────

func push_event(line: String) -> void:
	recent_events.append(line)
	while recent_events.size() > MAX_RECENT_EVENTS:
		recent_events.pop_front()


func add_fact(text: String) -> void:
	if text not in established_facts:
		established_facts.append(text)


func add_humiliation(id: String, text: String, weight: int, turn: int) -> void:
	humiliations.append(Humiliation.new(id, text, weight, turn))


func set_location(id: String, name: String) -> void:
	location = LogLocation.new(id, name)


func party_member(id: String) -> PartyMember:
	for m in party:
		if m.id == id:
			return m
	return null


func adjust_disposition(id: String, delta: int) -> bool:
	var m := party_member(id)
	if m == null:
		return false
	m.set_disposition(m.disposition + delta)
	return true


func set_quest_status(id: String, status: String) -> bool:
	for q in active_quests:
		if q.id == id:
			return q.set_status(status)
	return false


# ── serialisation ────────────────────────────────────────────────────────────

func to_dict() -> Dictionary:
	return {
		"schema_version": schema_version,
		"player": player.to_dict(),
		"location": location.to_dict(),
		"party": party.map(func(m): return m.to_dict()),
		"recent_events": recent_events.duplicate(),
		"established_facts": established_facts.duplicate(),
		"active_quests": active_quests.map(func(q): return q.to_dict()),
		"humiliations": humiliations.map(func(h): return h.to_dict()),
	}


static func from_dict(d: Dictionary) -> CanonLog:
	var v := int(d.get("schema_version", -1))
	if v != SCHEMA_VERSION:
		push_error("unsupported canon_log schema_version: %s" % v)
		return null
	var log := CanonLog.new()
	log.player = LogPlayer.from_dict(d.get("player", {}))
	log.location = LogLocation.from_dict(d.get("location", {}))
	for m in d.get("party", []):
		log.party.append(PartyMember.from_dict(m))
	for e in d.get("recent_events", []):
		log.push_event(e)
	for f in d.get("established_facts", []):
		log.add_fact(f)
	for q in d.get("active_quests", []):
		log.active_quests.append(Quest.from_dict(q))
	for h in d.get("humiliations", []):
		log.humiliations.append(Humiliation.from_dict(h))
	return log
