namespace Theriapolis.Core.Rules.Reputation;
///
/// Phase 6 M2 — append-only event log surfaced by the reputation screen
/// ("why does so-and-so hate me?"). Bounded to a reasonable tail so the
/// save file stays small even after a 100-hour playthrough.
///
/// Phase 6 M5 layers propagation on top: each entry will be re-walked
/// per game-day to fan out into other settlements with distance/time
/// decay.
///
public sealed class RepLedger
{
public const int MaxEntries = 256;
private readonly List _entries = new();
private int _nextSeq = 1;
public IReadOnlyList Entries => _entries;
public int Count => _entries.Count;
///
/// Append to the ledger. If
/// is 0, a fresh monotone id is assigned; otherwise the supplied id
/// is preserved (used by the save-restore path).
///
public RepEvent Append(RepEvent ev)
{
if (ev.SequenceId == 0)
ev = ev with { SequenceId = _nextSeq++ };
else if (ev.SequenceId >= _nextSeq)
_nextSeq = ev.SequenceId + 1;
_entries.Add(ev);
if (_entries.Count > MaxEntries) _entries.RemoveAt(0);
return ev;
}
public void Clear()
{
_entries.Clear();
_nextSeq = 1;
}
/// Largest issued so far. 0 = empty ledger.
public int HighestSequenceId => _nextSeq - 1;
/// Most recent N events affecting .
public IEnumerable ForFaction(string factionId, int count = 8)
{
int yielded = 0;
for (int i = _entries.Count - 1; i >= 0 && yielded < count; i--)
{
if (string.Equals(_entries[i].FactionId, factionId, System.StringComparison.OrdinalIgnoreCase))
{
yielded++;
yield return _entries[i];
}
}
}
/// Most recent N events affecting .
public IEnumerable ForRole(string roleTag, int count = 8)
{
int yielded = 0;
for (int i = _entries.Count - 1; i >= 0 && yielded < count; i--)
{
if (string.Equals(_entries[i].RoleTag, roleTag, System.StringComparison.OrdinalIgnoreCase))
{
yielded++;
yield return _entries[i];
}
}
}
}