using NeonSprawl.Server.Game.Skills;

namespace NeonSprawl.Server.Game.Mastery;

/// <summary>Applies dev-only mastery fixture resets (NEO-48).</summary>
public static class MasteryFixtureOperations
{
    /// <summary>Validates <paramref name="skillXp"/> entries; returns false when any key is blank or XP is negative.</summary>
    public static bool TryNormalizeSkillXp(
        IReadOnlyDictionary<string, int>? skillXp,
        out Dictionary<string, int> normalized)
    {
        normalized = new Dictionary<string, int>(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;
    }

    /// <summary>Applies skill XP (batch, then perk reset). Rolls back XP when perk reset fails after XP writes.</summary>
    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<string, int>(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;
    }
}
