import 'dart:convert'; import 'dart:io'; import 'package:flutter/services.dart'; import '../game/models/season.dart'; import 'remote/content_downloader.dart'; /// Resolves season content: bundled season 1 is always available offline; /// remotely synced packs in [cacheDir] extend or override it. Load errors on /// any single pack never break the list — worst case the player sees the /// bundled content. class ContentRepository { ContentRepository({this.cacheDir, ContentDownloader? downloader}) : _downloader = downloader; static const bundledSeasonIds = ['season_001']; final Directory? cacheDir; final ContentDownloader? _downloader; bool _syncedThisSession = false; Future loadBundledSeason(String seasonId) async { final raw = await rootBundle.loadString('assets/seasons/$seasonId/pack.json'); return SeasonPack.fromJson(jsonDecode(raw) as Map); } Future _loadCachedSeason(Directory dir) async { try { final file = File('${dir.path}/pack.json'); if (!await file.exists()) return null; return SeasonPack.fromJson( jsonDecode(await file.readAsString()) as Map); } catch (_) { return null; // Corrupt or future-schema pack: ignore. } } /// All playable seasons, sorted by seasonId. Cached packs win over the /// bundled copy of the same season (they may carry balance fixes). Future> availableSeasons() async { final byId = {}; for (final id in bundledSeasonIds) { try { byId[id] = await loadBundledSeason(id); } catch (_) { // Bundled pack should never fail; if it does, skip rather than crash. } } final seasonsDir = cacheDir == null ? null : Directory('${cacheDir!.path}/seasons'); if (seasonsDir != null && await seasonsDir.exists()) { await for (final entry in seasonsDir.list()) { if (entry is! Directory) continue; final pack = await _loadCachedSeason(entry); if (pack != null) byId[pack.seasonId] = pack; } } final list = byId.values.toList() ..sort((a, b) => a.seasonId.compareTo(b.seasonId)); return list; } /// Fire-once-per-session remote sync. Returns true when new content /// arrived (callers then re-read [availableSeasons]). Future refresh() async { final downloader = _downloader; if (downloader == null || _syncedThisSession) return false; _syncedThisSession = true; return downloader.sync(); } }