using System.Collections.Concurrent;
namespace NeonSprawl.Server.Game.Encounters;
/// Thread-safe in-memory encounter completion event records (NEO-107).
public sealed class InMemoryEncounterCompleteEventStore : IEncounterCompleteEventStore
{
private readonly ConcurrentDictionary eventsByKey = new(StringComparer.Ordinal);
private readonly ConcurrentDictionary keyLocks = new(StringComparer.Ordinal);
///
public bool TryRecord(EncounterCompleteEvent completeEvent)
{
var key = EncounterProgressIds.MakeProgressKey(completeEvent.PlayerId, completeEvent.EncounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
if (eventsByKey.ContainsKey(key))
{
return false;
}
eventsByKey[key] = completeEvent with
{
GrantedItems = [..completeEvent.GrantedItems],
};
return true;
}
}
///
public bool TryGet(string playerId, string encounterId, out EncounterCompleteEvent completeEvent)
{
completeEvent = default;
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
return eventsByKey.TryGetValue(key, out completeEvent);
}
}
/// Dev fixture only — removes one player+encounter event row (NEO-108 Bruno reset).
public bool TryClear(string playerId, string encounterId)
{
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
if (key.Length == 0)
{
return false;
}
lock (keyLocks.GetOrAdd(key, _ => new object()))
{
var removed = eventsByKey.TryRemove(key, out _);
keyLocks.TryRemove(key, out _);
return removed;
}
}
}