class_name NPCStateRecord
extends RefCounted

const SCHEMA_VERSION := 3
const LEGACY_SCHEMA_VERSION := 1
const PREVIOUS_SCHEMA_VERSION := 2

var data: Dictionary


func _init(record_data: Dictionary = {}) -> void:
	data = record_data.duplicate(true)


static func capture(npc: SimNPC) -> NPCStateRecord:
	return NPCStateRecord.new(
		{
			"schema_version": SCHEMA_VERSION,
			"id": npc.id,
			"name": npc.npc_name,
			"profession": String(npc.profession),
			"hunger": npc.hunger,
			"energy": npc.energy,
			"strength": npc.strength,
			"intelligence": npc.intelligence,
			"current_task": String(npc.current_task),
			"task_state": String(npc.task_state),
			"position": [npc.position.x, npc.position.y, npc.position.z],
			"is_starving": npc.is_starving,
			"starvation_ticks": npc.starvation_ticks,
			"starvation_death_threshold": npc.starvation_death_threshold,
			"is_dead": npc.is_dead,
			"task_duration": npc.task_duration,
			"task_progress": npc.task_progress,
			"task_complete": npc.task_complete,
			"target_id": String(npc.target_id),
			"travel_target_position":
			[
				npc.travel_target_position.x,
				npc.travel_target_position.y,
				npc.travel_target_position.z
			],
			"has_travel_target": npc.has_travel_target,
			"inventory": npc.inventory.duplicate(true),
			"last_task": String(npc.last_task),
			"random_seed": str(npc.random_source.seed),
			"random_state": str(npc.random_source.state)
		}
	)


static func from_dictionary(record_data: Dictionary) -> NPCStateRecord:
	var version := int(record_data.get("schema_version", -1))
	if version in [LEGACY_SCHEMA_VERSION, PREVIOUS_SCHEMA_VERSION]:
		record_data = _migrate_legacy(record_data, version)
	elif version != SCHEMA_VERSION:
		return null
	if not record_data.has_all(
		[
			"id",
			"name",
			"profession",
			"hunger",
			"energy",
			"strength",
			"intelligence",
			"current_task",
			"task_state",
			"position",
			"is_starving",
			"starvation_ticks",
			"starvation_death_threshold",
			"is_dead",
			"task_duration",
			"task_progress",
			"task_complete",
			"target_id",
			"travel_target_position",
			"has_travel_target",
			"inventory",
			"last_task",
			"random_seed",
			"random_state"
		]
	):
		return null
	var saved_position = record_data["position"]
	if not saved_position is Array or saved_position.size() != 3:
		return null
	var saved_target_position = record_data["travel_target_position"]
	if not saved_target_position is Array or saved_target_position.size() != 3:
		return null
	if not record_data["inventory"] is Dictionary:
		return null
	var profession_id := StringName(record_data["profession"])
	if SimulationDefinitions.get_profession(profession_id) == null:
		return null
	var action_id := StringName(record_data["current_task"])
	if (
		action_id not in [SimulationIds.ACTION_IDLE, SimulationIds.ACTION_DEAD]
		and SimulationDefinitions.get_action(action_id) == null
	):
		return null
	var last_action_id := StringName(record_data["last_task"])
	if not last_action_id.is_empty() and SimulationDefinitions.get_action(last_action_id) == null:
		return null
	return NPCStateRecord.new(record_data)


static func _migrate_legacy(legacy_data: Dictionary, version: int) -> Dictionary:
	var migrated := legacy_data.duplicate(true)
	migrated["schema_version"] = SCHEMA_VERSION
	if version == LEGACY_SCHEMA_VERSION:
		migrated["travel_target_position"] = legacy_data.get("position", [0.0, 0.0, 0.0])
		migrated["has_travel_target"] = false
	migrated["inventory"] = {}
	return migrated


func restore(debug_logs: bool) -> SimNPC:
	var random_source := RandomNumberGenerator.new()
	random_source.seed = String(data["random_seed"]).to_int()
	var npc := SimNPC.new(
		int(data["id"]),
		String(data["name"]),
		StringName(data["profession"]),
		float(data["strength"]),
		float(data["intelligence"]),
		random_source
	)
	var saved_position: Array = data["position"]
	var saved_target_position: Array = data["travel_target_position"]
	npc.hunger = float(data["hunger"])
	npc.energy = float(data["energy"])
	npc.current_task = StringName(data["current_task"])
	npc.task_state = StringName(data["task_state"])
	npc.position = Vector3(
		float(saved_position[0]), float(saved_position[1]), float(saved_position[2])
	)
	npc.is_starving = bool(data["is_starving"])
	npc.starvation_ticks = int(data["starvation_ticks"])
	npc.starvation_death_threshold = int(data["starvation_death_threshold"])
	npc.is_dead = bool(data["is_dead"])
	npc.task_duration = float(data["task_duration"])
	npc.task_progress = float(data["task_progress"])
	npc.task_complete = bool(data["task_complete"])
	npc.target_id = StringName(data["target_id"])
	npc.travel_target_position = Vector3(
		float(saved_target_position[0]),
		float(saved_target_position[1]),
		float(saved_target_position[2])
	)
	npc.has_travel_target = bool(data["has_travel_target"])
	npc.inventory = data["inventory"].duplicate(true)
	npc.last_task = StringName(data["last_task"])
	npc.random_source.state = String(data["random_state"]).to_int()
	npc.debug_logs = debug_logs
	return npc


func to_dictionary() -> Dictionary:
	return data.duplicate(true)
