using NeonSprawl.Server.Game.Skills;
namespace NeonSprawl.Server.Game.Mastery;
/// Applies dev-only mastery fixture resets (NEO-48).
public static class MasteryFixtureOperations
{
/// Validates entries; returns false when any key is blank or XP is negative.
public static bool TryNormalizeSkillXp(
IReadOnlyDictionary? skillXp,
out Dictionary normalized)
{
normalized = new Dictionary(StringComparer.Ordinal);
if (skillXp is null || skillXp.Count == 0)
{
return true;
}
foreach (var (skillId, xp) in skillXp)
{
var sid = skillId.Trim();
if (sid.Length == 0 || xp < 0)
{
normalized.Clear();
return false;
}
normalized[sid] = xp;
}
return true;
}
/// Applies skill XP (batch, then perk reset). Rolls back XP when perk reset fails after XP writes.
public static bool TryApply(
string playerId,
MasteryFixtureRequest body,
IPlayerPerkStateStore perkStore,
IPlayerSkillProgressionStore xpStore)
{
if (!TryNormalizeSkillXp(body.SkillXp, out var skillXp))
{
return false;
}
var previousXp = new Dictionary(xpStore.GetXpTotals(playerId), StringComparer.Ordinal);
if (skillXp.Count > 0 && !xpStore.TrySetSkillXpTotals(playerId, skillXp))
{
return false;
}
if (!body.ResetPerkState)
{
return true;
}
if (perkStore.TryResetPerkState(playerId))
{
return true;
}
if (skillXp.Count > 0)
{
_ = xpStore.TrySetSkillXpTotals(playerId, previousXp);
}
return false;
}
}