using System; using System.Collections; using System.Collections.Generic; using ShiroginSDK.Runtime.Core.SDK; using ShiroginSDK.Runtime.Core.Utils; using ShiroginSDK.Runtime.Modules.Data.Scripts; using ShiroginSDK.Runtime.Modules.Events; using ShiroginSDK.Runtime.Services.Base; using ShiroginSDK.Runtime.Services.Interfaces; using UnityEngine; #if SHIROGIN_FIREBASE_REMOTE_CONFIG using Firebase.RemoteConfig; #endif namespace ShiroginSDK.Runtime.Services.Implementations.RemoteConfig { #if SHIROGIN_FIREBASE_REMOTE_CONFIG /// /// Fetches and caches Firebase Remote Config data for ShiroginSDK. /// Automatically reads selected group and enable flags from SDKConfig. /// public class RemoteConfigService : ServiceBase, IRemoteConfigService { private ShiroginConfig _config; private string _configKey; private bool _initialized; // --------------------------------------------------------- // Value Accessors // --------------------------------------------------------- private string CachedJson { get { var dataService = ServiceLocator.Get(); return dataService?.Get()?.CachedJson ?? "{}"; } } public void ForceRefresh() { CoroutineRunner.Start(FetchAndCacheRoutine()); } public int GetInt(string key, int defaultValue = 0) { if (TryGetValue(key, out var raw) && int.TryParse(raw, out var val)) return val; return defaultValue; } public float GetFloat(string key, float defaultValue = 0f) { if (TryGetValue(key, out var raw) && float.TryParse(raw, out var val)) return val; return defaultValue; } public bool GetBool(string key, bool defaultValue = false) { if (TryGetValue(key, out var raw)) return raw.Equals("true", StringComparison.OrdinalIgnoreCase); return defaultValue; } public string GetString(string key, string defaultValue = "") { if (TryGetValue(key, out var raw)) return raw; return defaultValue; } // --------------------------------------------------------- // Lifecycle // --------------------------------------------------------- protected override void OnInitialize() { _config = ShiroginConfig.Load(); if (_config == null) { Debug.LogError("[RemoteConfigService] ❌ SDKConfig not found. Cannot initialize Remote Config."); return; } if (!_config.enableFirebaseRemoteConfig) { Debug.Log("[RemoteConfigService] ⚙️ Firebase Remote Config disabled in SDKConfig."); return; } _configKey = _config.selectedRemoteGroup; if (string.IsNullOrEmpty(_configKey)) _configKey = "DefaultGroup"; var eventService = ServiceLocator.Get(); eventService.Subscribe(OnFirebaseReady); Debug.Log($"[RemoteConfigService] ✅ Initialized. Waiting for Firebase. Target Group: '{_configKey}'"); } protected override void OnDispose() { var eventService = ServiceLocator.Get(); eventService.Unsubscribe(OnFirebaseReady); } // --------------------------------------------------------- // Firebase Ready // --------------------------------------------------------- private void OnFirebaseReady(ShiroginEvents.Firebase.FirebaseReadyEvent _) { if (!_config.enableFirebaseRemoteConfig) { Debug.Log("[RemoteConfigService] ⚙️ Firebase Remote Config disabled — skipping fetch."); return; } _initialized = true; Debug.Log($"[RemoteConfigService] 🔥 Firebase ready, fetching key '{_configKey}'..."); CoroutineRunner.Start(DelayedStart()); } private IEnumerator DelayedStart() { yield return new WaitForSeconds(1f); TryFetch(); } // --------------------------------------------------------- // Fetch Logic // --------------------------------------------------------- private void TryFetch() { var dataService = ServiceLocator.Get(); var data = dataService?.Get(); if (data == null) { Debug.LogError("[RemoteConfigService] ❌ RemoteConfigData not found in DataService!"); return; } if (!data.ShouldFetchNewData()) { Debug.Log("[RemoteConfigService] 🕓 Using cached Remote Config data (less than 24h old)."); TriggerEvent(data.CachedJson); return; } CoroutineRunner.Start(FetchAndCacheRoutine()); } public void Initialize(string selectedGroup) { throw new NotImplementedException(); } private IEnumerator FetchAndCacheRoutine() { if (!_initialized) { Debug.LogWarning("[RemoteConfigService] ⚠️ Firebase not initialized yet."); yield break; } Debug.Log($"[RemoteConfigService] 🔄 Fetching Remote Config key '{_configKey}'..."); var fetchTask = FirebaseRemoteConfig.DefaultInstance.FetchAsync(TimeSpan.Zero); var start = Time.realtimeSinceStartup; const float TIMEOUT = 10f; while (!fetchTask.IsCompleted && Time.realtimeSinceStartup - start < TIMEOUT) yield return null; if (!fetchTask.IsCompleted) { Debug.LogWarning("[RemoteConfigService] ⚠️ Fetch timeout. Using cached data."); TriggerEvent(ServiceLocator.Get()?.Get()?.CachedJson ?? "{}"); yield break; } if (fetchTask.IsFaulted) { Debug.LogError( $"[RemoteConfigService] ❌ Fetch error: {fetchTask.Exception?.GetBaseException().Message}"); TriggerEvent(ServiceLocator.Get()?.Get()?.CachedJson ?? "{}"); yield break; } var activateTask = FirebaseRemoteConfig.DefaultInstance.ActivateAsync(); while (!activateTask.IsCompleted) yield return null; ExtractAndCacheValues(); Debug.Log("RemoteConfig Service Dummy"); } // --------------------------------------------------------- // Extract + Cache + Reload // --------------------------------------------------------- private void ExtractAndCacheValues() { try { var rawJson = FirebaseRemoteConfig.DefaultInstance.GetValue(_configKey).StringValue; if (string.IsNullOrEmpty(rawJson)) { Debug.LogWarning($"[RemoteConfigService] ⚠️ RemoteConfig key '{_configKey}' returned empty JSON."); return; } var dataService = ServiceLocator.Get(); var data = dataService?.Get(); if (data == null) { Debug.LogError("[RemoteConfigService] ❌ RemoteConfigData not found in DataService!"); return; } if (data.CachedJson == rawJson) { Debug.Log("[RemoteConfigService] 🔁 No changes detected. Cached data is up-to-date."); return; } data.UpdateCache(rawJson); try { data.Load(); data.Initialize(); Debug.Log("[RemoteConfigService] 🔁 RemoteConfigData re-initialized with latest Firebase values."); } catch (Exception reinitEx) { Debug.LogError($"[RemoteConfigService] ❌ RemoteConfigData re-init failed: {reinitEx.Message}"); } TriggerEvent(rawJson); Debug.Log($"[RemoteConfigService] ✅ Remote Config key '{_configKey}' updated and applied."); } catch (Exception ex) { Debug.LogError($"[RemoteConfigService] ❌ ExtractAndCacheValues failed: {ex.Message}"); } } // --------------------------------------------------------- // Event Dispatch // --------------------------------------------------------- private void TriggerEvent(string json) { try { var eventService = ServiceLocator.Get(); eventService.Invoke( new ShiroginEvents.RemoteConfig.RemoteConfigUpdatedEvent(json, new Dictionary())); } catch (Exception ex) { Debug.LogError($"[RemoteConfigService] ⚠️ RemoteConfigUpdatedEvent failed: {ex.Message}"); } } private bool TryGetValue(string key, out string value) { value = string.Empty; var json = CachedJson; if (string.IsNullOrEmpty(json) || string.IsNullOrEmpty(key)) return false; key = $"\"{key}\""; var index = json.IndexOf(key, StringComparison.Ordinal); if (index == -1) return false; var colon = json.IndexOf(':', index); if (colon == -1) return false; var comma = json.IndexOf(',', colon); var end = comma == -1 ? json.IndexOf('}', colon) : comma; if (end == -1) end = json.Length - 1; var raw = json.Substring(colon + 1, end - colon - 1).Trim().Trim('"'); value = raw; return true; } } #else /// /// Fetches and caches Firebase Remote Config data for ShiroginSDK. /// Automatically reads selected group and enable flags from SDKConfig. /// public class RemoteConfigService : ServiceBase, IRemoteConfigService { private ShiroginConfig _config; private string _configKey; private bool _initialized; // --------------------------------------------------------- // Value Accessors // --------------------------------------------------------- private string CachedJson { get { var dataService = ServiceLocator.Get(); return dataService?.Get()?.CachedJson ?? "{}"; } } public void ForceRefresh() { CoroutineRunner.Start(FetchAndCacheRoutine()); } public int GetInt(string key, int defaultValue = 0) { if (TryGetValue(key, out var raw) && int.TryParse(raw, out var val)) return val; return defaultValue; } public float GetFloat(string key, float defaultValue = 0f) { if (TryGetValue(key, out var raw) && float.TryParse(raw, out var val)) return val; return defaultValue; } public bool GetBool(string key, bool defaultValue = false) { if (TryGetValue(key, out var raw)) return raw.Equals("true", StringComparison.OrdinalIgnoreCase); return defaultValue; } public string GetString(string key, string defaultValue = "") { if (TryGetValue(key, out var raw)) return raw; return defaultValue; } // --------------------------------------------------------- // Lifecycle // --------------------------------------------------------- protected override void OnInitialize() { _config = ShiroginConfig.Load(); if (_config == null) { Debug.LogError("[RemoteConfigService] ❌ SDKConfig not found. Cannot initialize Remote Config."); return; } _configKey = _config.selectedRemoteGroup; if (string.IsNullOrEmpty(_configKey)) _configKey = "DefaultGroup"; var eventService = ServiceLocator.Get(); eventService.Subscribe(OnFirebaseReady); Debug.Log($"[RemoteConfigService] Dummy Initialized. Waiting for Firebase. Target Group: '{_configKey}'"); } protected override void OnDispose() { var eventService = ServiceLocator.Get(); eventService.Unsubscribe(OnFirebaseReady); } // --------------------------------------------------------- // Firebase Ready // --------------------------------------------------------- private void OnFirebaseReady(ShiroginEvents.Firebase.FirebaseReadyEvent _) { _initialized = true; Debug.Log($"[RemoteConfigService] Dummy Firebase ready, fetching key '{_configKey}'..."); CoroutineRunner.Start(DelayedStart()); } private IEnumerator DelayedStart() { yield return new WaitForSeconds(1f); TryFetch(); } // --------------------------------------------------------- // Fetch Logic // --------------------------------------------------------- private void TryFetch() { var dataService = ServiceLocator.Get(); var data = dataService?.Get(); if (data == null) { Debug.LogError("[RemoteConfigService] ❌ RemoteConfigData not found in DataService!"); return; } if (!data.ShouldFetchNewData()) { Debug.Log("[RemoteConfigService] 🕓 Using cached Remote Config data (less than 24h old)."); TriggerEvent(data.CachedJson); return; } CoroutineRunner.Start(FetchAndCacheRoutine()); } public void Initialize(string selectedGroup) { throw new NotImplementedException(); } private IEnumerator FetchAndCacheRoutine() { if (!_initialized) { Debug.LogWarning("[RemoteConfigService] ⚠️ Firebase not initialized yet."); yield break; } Debug.Log("RemoteConfigService Dummy FetchAndCacheRoutine"); ExtractAndCacheValues(); Debug.Log("RemoteConfig Service Dummy"); } // --------------------------------------------------------- // Extract + Cache + Reload // --------------------------------------------------------- private void ExtractAndCacheValues() { Debug.Log("RemoteConfigService Dummy ExtractAndCacheValues"); } // --------------------------------------------------------- // Event Dispatch // --------------------------------------------------------- private void TriggerEvent(string json) { try { var eventService = ServiceLocator.Get(); eventService.Invoke( new ShiroginEvents.RemoteConfig.RemoteConfigUpdatedEvent(json, new Dictionary())); } catch (Exception ex) { Debug.LogError($"[RemoteConfigService] ⚠️ RemoteConfigUpdatedEvent failed: {ex.Message}"); } } private bool TryGetValue(string key, out string value) { value = string.Empty; var json = CachedJson; if (string.IsNullOrEmpty(json) || string.IsNullOrEmpty(key)) return false; key = $"\"{key}\""; var index = json.IndexOf(key, StringComparison.Ordinal); if (index == -1) return false; var colon = json.IndexOf(':', index); if (colon == -1) return false; var comma = json.IndexOf(',', colon); var end = comma == -1 ? json.IndexOf('}', colon) : comma; if (end == -1) end = json.Length - 1; var raw = json.Substring(colon + 1, end - colon - 1).Trim().Trim('"'); value = raw; return true; } } #endif }