using System;
using System.Collections.Generic;
using UnityEngine;
namespace MazeGen
{
///
/// Tracks one agent's progress through the maze: current room, ordered path history
/// (for visualization/backtracking), and O(1) sets for "have I been here" / "have I
/// used this exit" checks. Plain C# class - no MonoBehaviour/scene dependency, so it's
/// cheap to run many of these (e.g. for future autonomous multi-agent scenarios).
///
/// Also tracks the agent's combat state (character sheet, health, equipment).
///
public class MazeAgent
{
public readonly string Name;
public Room CurrentRoom { get; private set; }
public int NewRoomsVisited { get; private set; }
public bool IsFinished { get; private set; }
/// The exit the agent walked through to reach CurrentRoom, if any (null for the starting room). Used by AutonomousAgentBrain to avoid immediately reversing.
public Exit LastUsedExit { get; private set; }
/// This agent's character sheet (stats, equipment, inventory).
public CharacterSheet CharacterSheet { get; private set; }
///
/// Track if this agent died (different from IsFinished, which means reached the exit).
/// Backed by its own flag rather than derived from health so that Die() stays a
/// once-only, event-firing transition even though combat has already zeroed the health
/// by the time it's called.
///
public bool IsDead { get; private set; }
///
/// True while this agent is committed to a fight (mustering or being resolved). Its brain
/// parks itself so it can't wander off out of a fight it already joined.
///
public bool IsEngaged { get; set; }
/// What this agent is thinking - decisions, peeks, fight blow-by-blow. Observation only.
public AgentThoughtLog Thoughts { get; } = new AgentThoughtLog();
/// Name to show a human: the generated character name, not the GameObject name.
public string DisplayName => CharacterSheet?.Name ?? Name;
private readonly List _path = new List();
private readonly HashSet _visitedRoomIds = new HashSet();
private readonly HashSet _usedExitIds = new HashSet();
private readonly Dictionary _visitCounts = new Dictionary();
private readonly HashSet _fledRoomIds = new HashSet();
public IReadOnlyList PathHistory => _path;
public event Action RoomEntered;
public event Action ReachedExitRoom;
public event Action Died;
public MazeAgent(GameObject go, Room startRoom, CharacterSheet characterSheet = null)
{
go.tag = "Agent";
Name = go.name;
CharacterSheet = characterSheet ?? new CharacterSheet(go, new CharacterStats());
EnterRoom(startRoom, null);
}
public bool HasVisited(int roomId) => _visitedRoomIds.Contains(roomId);
public bool HasUsedExit(Exit exit) => _usedExitIds.Contains(exit.Id);
///
/// Remembers that a fight in this room went nowhere (a stalemate), so the brain leaves
/// instead of re-declaring the same unwinnable fight every decision tick forever.
///
public void NoteFledFrom(int roomId) => _fledRoomIds.Add(roomId);
public bool HasFledFrom(int roomId) => _fledRoomIds.Contains(roomId);
/// How many times this agent has personally entered the given room (0 if never).
public int VisitCount(int roomId) => _visitCounts.TryGetValue(roomId, out var count) ? count : 0;
public void EnterRoom(Room room, Exit viaExit)
{
// Check if agent is already dead before entering
if (IsDead)
return;
if (viaExit != null)
_usedExitIds.Add(viaExit.Id);
if (_visitedRoomIds.Add(room.Id))
NewRoomsVisited++;
_visitCounts[room.Id] = VisitCount(room.Id) + 1;
CurrentRoom = room;
LastUsedExit = viaExit;
_path.Add(room);
RoomEntered?.Invoke(room);
if (room.Kind == RoomKind.Exit)
{
IsFinished = true;
Thoughts.Add(ThoughtKind.Goal, $"Reached the exit room {room.Id} - done, {NewRoomsVisited} room(s) seen.");
ReachedExitRoom?.Invoke(room);
}
}
/// Called when the agent dies (defeated in combat or other hazard). Stops them from moving.
public void Die()
{
if (IsDead) return;
IsDead = true;
IsEngaged = false;
CharacterSheet.Stats.CurrentHealth = 0;
Thoughts.Add(ThoughtKind.Goal, $"Died in room {CurrentRoom?.Id.ToString() ?? "?"}.");
Died?.Invoke();
}
}
}