using SVSim.Database.Models; namespace SVSim.Database.Services; public class CurrencySpendService : ICurrencySpendService { private readonly IViewerEntitlements _entitlements; public CurrencySpendService(IViewerEntitlements entitlements) => _entitlements = entitlements; public Task TrySpendAsync(Viewer viewer, SpendCurrency currency, long cost, CancellationToken ct = default) { if (cost < 0) cost = 0; // Freeplay bypass applies only to the three main currencies; SpotPoint always real. if (_entitlements.IsFreeplay && currency != SpendCurrency.SpotPoint) { return Task.FromResult(new SpendResult( SpendOutcome.Success, _entitlements.EffectiveBalance(viewer, currency))); } ulong current = GetBalance(viewer, currency); if (current < (ulong)cost) return Task.FromResult(new SpendResult(SpendOutcome.Insufficient, (long)current)); ulong post = current - (ulong)cost; SetBalance(viewer, currency, post); return Task.FromResult(new SpendResult(SpendOutcome.Success, (long)post)); } private static ulong GetBalance(Viewer v, SpendCurrency c) => c switch { SpendCurrency.Crystal => v.Currency.Crystals, SpendCurrency.Rupee => v.Currency.Rupees, SpendCurrency.RedEther => v.Currency.RedEther, SpendCurrency.SpotPoint => v.Currency.SpotPoints, _ => throw new ArgumentOutOfRangeException(nameof(c)), }; private static void SetBalance(Viewer v, SpendCurrency c, ulong value) { switch (c) { case SpendCurrency.Crystal: v.Currency.Crystals = value; break; case SpendCurrency.Rupee: v.Currency.Rupees = value; break; case SpendCurrency.RedEther: v.Currency.RedEther = value; break; case SpendCurrency.SpotPoint: v.Currency.SpotPoints = value; break; default: throw new ArgumentOutOfRangeException(nameof(c)); } } }