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<SeasonPack> loadBundledSeason(String seasonId) async {
    final raw =
        await rootBundle.loadString('assets/seasons/$seasonId/pack.json');
    return SeasonPack.fromJson(jsonDecode(raw) as Map<String, dynamic>);
  }

  Future<SeasonPack?> _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<String, dynamic>);
    } 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<List<SeasonPack>> availableSeasons() async {
    final byId = <String, SeasonPack>{};
    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<bool> refresh() async {
    final downloader = _downloader;
    if (downloader == null || _syncedThisSession) return false;
    _syncedThisSession = true;
    return downloader.sync();
  }
}
