using System; using System.Collections.Generic; using System.Linq; using BriarQueen.Data.IO.Saves; using BriarQueen.Framework.Managers.IO; using BriarQueen.Framework.Services.Puzzles.Base; using Cysharp.Threading.Tasks; using UnityEngine; using VContainer; namespace BriarQueen.Framework.Services.Puzzles { public class PuzzleService : IDisposable { private readonly SaveManager _saveManager; private readonly Dictionary _activePuzzles = new(); private bool _isWritingState; [Inject] public PuzzleService(SaveManager saveManager) { _saveManager = saveManager; _saveManager.OnBeforeSaveRequestedAsync += OnBeforeSaveRequestedAsync; } public void Dispose() { _saveManager.OnBeforeSaveRequestedAsync -= OnBeforeSaveRequestedAsync; } public async UniTask LoadPuzzle(BasePuzzle basePuzzle) { if (basePuzzle == null) { return; } if (string.IsNullOrWhiteSpace(basePuzzle.PuzzleID)) { Debug.LogWarning($"[PuzzleService] Cannot load puzzle '{basePuzzle.name}' with null/empty PuzzleID."); return; } _activePuzzles[basePuzzle.PuzzleID] = basePuzzle; await basePuzzle.PostLoad(); await TryRestorePuzzleState(basePuzzle); } public async UniTask LoadPuzzles(IEnumerable basePuzzles) { _activePuzzles.Clear(); if (basePuzzles == null) { return; } foreach (var basePuzzle in basePuzzles) { await LoadPuzzle(basePuzzle); } } public async UniTask SavePuzzle(BasePuzzle basePuzzle) { if (basePuzzle == null) { return; } if (!string.IsNullOrWhiteSpace(basePuzzle.PuzzleID) && _activePuzzles.TryGetValue(basePuzzle.PuzzleID, out var activePuzzle) && activePuzzle != basePuzzle) { return; } await SavePuzzleState(basePuzzle, flushToDisk: true); await basePuzzle.PreUnload(); if (!string.IsNullOrWhiteSpace(basePuzzle.PuzzleID)) { _activePuzzles.Remove(basePuzzle.PuzzleID); } } public async UniTask SavePuzzles(IEnumerable basePuzzles) { if (basePuzzles != null) { foreach (var basePuzzle in basePuzzles) { await SavePuzzle(basePuzzle); } } _activePuzzles.Clear(); } private async UniTask OnBeforeSaveRequestedAsync() { if (_activePuzzles.Count == 0) { return; } foreach (var basePuzzle in _activePuzzles.Values.ToList()) { await SavePuzzleState(basePuzzle, flushToDisk: false); } } private async UniTask TryRestorePuzzleState(BasePuzzle basePuzzle) { if (basePuzzle == null || _saveManager.CurrentSave == null) { return; } if (basePuzzle is not IPuzzleStateful stateful) { return; } var save = _saveManager.CurrentSave; var entry = save.PuzzleStates?.FirstOrDefault(x => x != null && x.PuzzleID == basePuzzle.PuzzleID); var stateBlob = entry?.State; try { await stateful.RestoreState(stateBlob); } catch (Exception ex) { Debug.LogWarning($"[PuzzleService] Failed to restore state for '{basePuzzle.PuzzleID}': {ex}"); } } private async UniTask SavePuzzleState(BasePuzzle basePuzzle, bool flushToDisk) { if (basePuzzle == null || _saveManager.CurrentSave == null) { return; } if (basePuzzle is not IPuzzleStateful stateful) { return; } if (_isWritingState) { return; } _isWritingState = true; try { if (basePuzzle is IPuzzleWorldStateSync worldStateSync) { worldStateSync.SyncWorldStateToSave(); } var save = _saveManager.CurrentSave; save.PuzzleStates ??= new List(); byte[] blob; try { blob = await stateful.CaptureState() ?? Array.Empty(); } catch (Exception ex) { Debug.LogWarning($"[PuzzleService] Failed to capture state for '{basePuzzle.PuzzleID}': {ex}"); return; } var existing = save.PuzzleStates.FirstOrDefault(x => x != null && x.PuzzleID == basePuzzle.PuzzleID); if (existing == null) { existing = new PuzzleStateSaveData { PuzzleID = basePuzzle.PuzzleID }; save.PuzzleStates.Add(existing); } existing.State = blob; existing.Completed = stateful.IsCompleted; if (flushToDisk) { await _saveManager.SaveGameDataLatest(); } } finally { _isWritingState = false; } } } }