import 'dart:convert';
import 'dart:io';

import 'package:block_seasons/data/content_repository.dart';
import 'package:block_seasons/game/models/season.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  test('loads the bundled Season 1 pack with 60 calibrated stages', () async {
    final repo = ContentRepository();
    final pack = await repo.loadBundledSeason('season_001');
    expect(pack.seasonId, 'season_001');
    expect(pack.stages, hasLength(60));
    expect(pack.titleFor('ko'), '첫 개화');

    // Sanity: every stage has a positive budget and at least one objective.
    for (final stage in pack.stages) {
      expect(stage.moveLimit, greaterThan(0), reason: stage.id);
      expect(stage.objectives, isNotEmpty, reason: stage.id);
      expect(stage.stars.threeMovesLeft, greaterThan(stage.stars.twoMovesLeft),
          reason: stage.id);
    }
  });

  test('availableSeasons returns the bundled season', () async {
    final repo = ContentRepository();
    final seasons = await repo.availableSeasons();
    expect(seasons.map((s) => s.seasonId), contains('season_001'));
  });

  group('cache merge and fallback', () {
    late Directory tmp;

    setUp(() async {
      tmp = await Directory.systemTemp.createTemp('bs_repo_');
    });

    tearDown(() async {
      if (await tmp.exists()) await tmp.delete(recursive: true);
    });

    Map<String, dynamic> packJson(String id) => {
          'schemaVersion': 1,
          'seasonId': id,
          'version': 1,
          'title': {'en': id},
          'theme': const SeasonTheme().toJson(),
          'stages': [
            {
              'id': '${id}_001',
              'seed': 1,
              'moveLimit': 10,
              'preset': [],
              'objectives': [
                {'type': 'reachScore', 'target': 100}
              ],
              'stars': {
                'two': {'movesLeft': 2},
                'three': {'movesLeft': 4}
              },
              'generatorProfile': 'mid',
            }
          ],
        };

    Future<void> putCachedSeason(String id) async {
      final dir = Directory('${tmp.path}/seasons/$id');
      await dir.create(recursive: true);
      await File('${dir.path}/pack.json')
          .writeAsString(jsonEncode(packJson(id)));
    }

    test('merges cached seasons after bundled ones, sorted by id', () async {
      TestWidgetsFlutterBinding.ensureInitialized();
      await putCachedSeason('season_002');
      final repo = ContentRepository(cacheDir: tmp);
      final seasons = await repo.availableSeasons();
      expect(seasons.map((s) => s.seasonId).toList(),
          ['season_001', 'season_002']);
    });

    test('cached copy of a bundled season wins over the bundle', () async {
      TestWidgetsFlutterBinding.ensureInitialized();
      await putCachedSeason('season_001');
      final repo = ContentRepository(cacheDir: tmp);
      final seasons = await repo.availableSeasons();
      // Cached fake has exactly 1 stage; the bundled real one has 60.
      expect(seasons.single.stages, hasLength(1));
    });

    test('corrupt cached pack falls back to bundle silently', () async {
      TestWidgetsFlutterBinding.ensureInitialized();
      final dir = Directory('${tmp.path}/seasons/season_001');
      await dir.create(recursive: true);
      await File('${dir.path}/pack.json').writeAsString('{not json');
      final repo = ContentRepository(cacheDir: tmp);
      final seasons = await repo.availableSeasons();
      expect(seasons.single.stages, hasLength(60));
    });
  });
}
