using System; using System.Collections.Generic; using ShiroginSDK.Runtime.Modules.IAP.Enums; using UnityEngine; namespace ShiroginSDK.Runtime.Modules.Data.Scripts { [Serializable] public class ProductPurchase { public string ProductId; public int PurchaseCount; public long LastPurchaseUnix; public StoreCategory StoreCategory; public ProductPurchase(string id, int count, long unix, StoreCategory type = StoreCategory.UnknownPacks) { ProductId = id; PurchaseCount = count; LastPurchaseUnix = unix; StoreCategory = type; } } [Serializable] public class LimitedOfferState { public string OfferId; public bool Seen; public bool Purchased; public long ExpireUnix; // 0 = unlimited public bool IsActive(long nowUnix) { return ExpireUnix == 0 || nowUnix < ExpireUnix; } } [Serializable] public class StoreData : BaseData { public const string REMOVE_ADS_PRODUCT_ID = "remove_ads"; public List Purchases = new(); public List LimitedOffers = new(); public long DailyShopNextRefreshUnix; protected override string PrefsKey => "STORE_DATA"; // -------------------------------------------------------------------- // ๐Ÿšซ REMOVE ADS // -------------------------------------------------------------------- public bool HasRemovedAds => HasPurchased(REMOVE_ADS_PRODUCT_ID); private static long NowUnix() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } private ProductPurchase FindPurchase(string pid) { return Purchases.Find(p => p.ProductId == pid); } private LimitedOfferState FindOffer(string oid) { return LimitedOffers.Find(o => o.OfferId == oid); } // -------------------------------------------------------------------- // ๐Ÿงพ PURCHASE MANAGEMENT // -------------------------------------------------------------------- public void MarkPurchased(string productId, int count = 1, StoreCategory type = StoreCategory.UnknownPacks) { if (string.IsNullOrEmpty(productId)) { Debug.LogWarning("[StoreData] Tried to mark a purchase with null/empty productId."); return; } var existing = FindPurchase(productId); if (existing == null) { Purchases.Add(new ProductPurchase(productId, Mathf.Max(1, count), NowUnix(), type)); } else { existing.PurchaseCount += Mathf.Max(1, count); existing.LastPurchaseUnix = NowUnix(); existing.StoreCategory = type; } Save(); Debug.Log($"[StoreData] โœ… Purchase marked โ†’ {productId} ({type})"); } public int GetPurchaseCount(string productId) { return FindPurchase(productId)?.PurchaseCount ?? 0; } public bool HasPurchased(string productId) { return GetPurchaseCount(productId) > 0; } public int GetTotalPurchased(StoreCategory type) { var total = 0; foreach (var p in Purchases) if (p.StoreCategory == type) total += p.PurchaseCount; return total; } public bool HasPurchasedType(StoreCategory type) { return Purchases.Exists(p => p.StoreCategory == type); } public void ResetPurchases(StoreCategory type) { Purchases.RemoveAll(p => p.StoreCategory == type); Save(); Debug.Log($"[StoreData] ๐Ÿ”„ Reset purchases of type {type}"); } public void SetRemoveAds(bool purchased) { if (purchased) MarkPurchased(REMOVE_ADS_PRODUCT_ID, 1, StoreCategory.RemoveAdsPacks); else Purchases.RemoveAll(p => p.ProductId == REMOVE_ADS_PRODUCT_ID); Save(); Debug.Log($"[StoreData] ๐Ÿงฉ Remove Ads state updated โ†’ {purchased}"); } // -------------------------------------------------------------------- // ๐Ÿ•’ DAILY SHOP // -------------------------------------------------------------------- public void SetDailyShopRefresh(long nextUnix) { DailyShopNextRefreshUnix = nextUnix; Save(); } public bool NeedsDailyShopRefresh(long nowUnix) { return nowUnix >= DailyShopNextRefreshUnix; } public void ForceRefreshNow(int hoursToNext = 24) { DailyShopNextRefreshUnix = NowUnix() + hoursToNext * 3600L; Save(); } // -------------------------------------------------------------------- // ๐ŸŽ LIMITED OFFERS // -------------------------------------------------------------------- public void UpsertOffer(string offerId, long expireUnix = 0) { var offer = FindOffer(offerId); if (offer == null) LimitedOffers.Add(new LimitedOfferState { OfferId = offerId, Seen = false, Purchased = false, ExpireUnix = expireUnix }); else offer.ExpireUnix = expireUnix; Save(); } public void MarkOfferSeen(string offerId) { var offer = FindOffer(offerId); if (offer == null) return; offer.Seen = true; Save(); } public bool IsOfferActive(string offerId) { var offer = FindOffer(offerId); return offer != null && offer.IsActive(NowUnix()); } public void MarkOfferPurchased(string offerId) { var offer = FindOffer(offerId); if (offer == null) return; offer.Purchased = true; Save(); } } }