using System;
using System.Collections.Generic;
using asap.core;
using UnityEngine;
namespace Activity
{
///
/// 入账/扣账成功后由 发布,
/// 供各活动自行判断是否刷新入口合并红点等(避免订阅者与具体玩法耦合)。
///
public readonly struct ActivityEventTokenBalanceChanged
{
public int EventId { get; }
public ActivityEventTokenBalanceChanged(int eventId)
{
EventId = eventId;
}
}
}
namespace Activity.Services
{
///
/// 活动代币服务实现
/// 负责管理所有活动的代币数据,提供统一的存储接口
///
public class EventTokenService : IEventTokenService
{
#region 常量与字段
private const string TransitionDataKey = "TransitionData";
private Dictionary _tokenData = new Dictionary();
private readonly object _lock = new object();
private bool _isInitialized;
private readonly game.PlayerDataStorage.IPlayerSaveStore store;
public EventTokenService(game.PlayerDataStorage.IPlayerSaveStore store)
{ this.store = store ?? throw new ArgumentNullException(nameof(store)); }
#endregion
#region 初始化
public void Initialize()
{
lock (_lock)
{
if (!store.IsCurrent) throw new InvalidOperationException("Activity token session is no longer current.");
if (_isInitialized) return;
var read = store.Read(TransitionDataKey);
var value = read.ValueOrThrow();
Dictionary loaded;
if (read.Found == false) loaded = new Dictionary();
else
{
if (string.IsNullOrWhiteSpace(value))
throw new Newtonsoft.Json.JsonSerializationException("Stored activity tokens cannot be empty.");
loaded = Newtonsoft.Json.JsonConvert.DeserializeObject>(value);
if (loaded == null) throw new Newtonsoft.Json.JsonSerializationException("Stored activity tokens cannot be null.");
}
_tokenData = loaded;
_isInitialized = true;
}
}
// Publish the candidate balance only after the current store accepts its exact JSON.
// Acceptance remains distinct from cloud confirmation, which belongs to the enclosing action.
private void SaveTokens(Dictionary candidate)
{
store.Write(TransitionDataKey, Newtonsoft.Json.JsonConvert.SerializeObject(candidate)).RequireAccepted();
_tokenData = candidate;
}
#endregion
#region 基础操作
public void AddToken(int eventId, int count)
{
if (eventId <= 0)
{
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
return;
}
if (count == 0)
return;
lock (_lock)
{
try
{
EnsureInitialized();
var currentCount = GetTokenCount(eventId);
var candidate = new Dictionary(_tokenData);
candidate[eventId] = currentCount + count;
SaveTokens(candidate);
#if UNITY_EDITOR
Debug.Log($"[EventTokenService] Added {count} tokens to event {eventId}, new total: {_tokenData[eventId]}");
#endif
}
catch (Exception e)
{
Debug.LogError($"[EventTokenService] AddToken failed for event {eventId}: {e.Message}");
throw;
}
}
PublishActivityEventTokenBalanceChanged(eventId);
}
public int RemoveToken(int eventId, int count)
{
if (eventId <= 0)
{
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
return 0;
}
int actualRemoved = 0;
lock (_lock)
{
try
{
EnsureInitialized();
var currentCount = GetTokenCount(eventId);
actualRemoved = Mathf.Min(count, currentCount);
var newCount = currentCount - actualRemoved;
var candidate = new Dictionary(_tokenData);
if (newCount <= 0)
{
candidate.Remove(eventId);
}
else
{
candidate[eventId] = newCount;
}
SaveTokens(candidate);
#if UNITY_EDITOR
Debug.Log($"[EventTokenService] Removed {actualRemoved} tokens from event {eventId}, remaining: {newCount}");
#endif
}
catch (Exception e)
{
Debug.LogError($"[EventTokenService] RemoveToken failed for event {eventId}: {e.Message}");
throw;
}
}
PublishActivityEventTokenBalanceChanged(eventId);
return actualRemoved;
}
public int GetTokenCount(int eventId)
{
if (eventId <= 0) return 0;
lock (_lock)
{
try
{
EnsureInitialized();
return _tokenData.GetValueOrDefault(eventId, 0);
}
catch (Exception e)
{
Debug.LogError($"[EventTokenService] GetTokenCount failed for event {eventId}: {e.Message}");
throw;
}
}
}
public void SetToken(int eventId, int count)
{
if (eventId <= 0)
{
Debug.LogWarning($"[EventTokenService] Invalid eventId: {eventId}");
return;
}
lock (_lock)
{
try
{
EnsureInitialized();
var candidate = new Dictionary(_tokenData);
candidate[eventId] = count;
SaveTokens(candidate);
#if UNITY_EDITOR
Debug.Log($"[EventTokenService] Set event {eventId} token count to {count}");
#endif
}
catch (Exception e)
{
Debug.LogError($"[EventTokenService] SetToken failed for event {eventId}: {e.Message}");
throw;
}
}
}
public void ClearToken(int eventId)
{
if (eventId <= 0) return;
lock (_lock)
{
try
{
EnsureInitialized();
var candidate = new Dictionary(_tokenData);
if (candidate.Remove(eventId))
{
SaveTokens(candidate);
}
}
catch (Exception e)
{
Debug.LogError($"[EventTokenService] ClearToken failed for event {eventId}: {e.Message}");
throw;
}
}
}
#endregion
#region 批量操作
public Dictionary GetAllTokens()
{
lock (_lock)
{
EnsureInitialized();
return new Dictionary(_tokenData);
}
}
public void SetTokens(Dictionary tokens)
{
if (tokens == null) return;
lock (_lock)
{
try
{
EnsureInitialized();
var candidate = new Dictionary();
foreach (var kvp in tokens)
{
if (kvp.Key > 0)
{
candidate[kvp.Key] = kvp.Value;
}
}
SaveTokens(candidate);
#if UNITY_EDITOR
Debug.Log($"[EventTokenService] Set {tokens.Count} token entries");
#endif
}
catch (Exception e)
{
Debug.LogError($"[EventTokenService] SetTokens failed: {e.Message}");
throw;
}
}
}
public void ClearTokens(IEnumerable eventIds)
{
if (eventIds == null) return;
lock (_lock)
{
try
{
EnsureInitialized();
var candidate = new Dictionary(_tokenData);
bool changed = false;
foreach (var eventId in eventIds)
{
if (candidate.Remove(eventId))
{
changed = true;
}
}
if (changed)
{
SaveTokens(candidate);
}
}
catch (Exception e)
{
Debug.LogError($"[EventTokenService] ClearTokens failed: {e.Message}");
throw;
}
}
}
#endregion
#region 私有辅助方法
private void EnsureInitialized()
{
Initialize();
}
private static void PublishActivityEventTokenBalanceChanged(int eventId)
{
try
{
GContext.Publish(new Activity.ActivityEventTokenBalanceChanged(eventId));
}
catch
{
// GContext 未就绪等
}
}
#endregion
}
}