import 'package:block_seasons/core/rng.dart';
import 'package:block_seasons/game/engine/game_engine.dart';
import 'package:block_seasons/game/models/grid.dart';
import 'package:block_seasons/game/models/stage.dart';
import 'package:flutter_test/flutter_test.dart';

StageConfig _endlessStage(int seed) => StageConfig.fromJson({
      'id': 'stress_$seed',
      'seed': seed,
      'moveLimit': 40,
      'preset': const <Map<String, dynamic>>[],
      'objectives': [
        {'type': 'reachScore', 'target': 99999999},
      ],
      'stars': {
        'two': {'movesLeft': 5},
        'three': {'movesLeft': 10},
      },
      'generatorProfile': 'mid',
    });

void main() {
  test('100 seeded random games run to a terminal state without violations',
      () {
    for (var seed = 0; seed < 100; seed++) {
      final engine = GameEngine(_endlessStage(seed));
      final rng = SeededRng(seed * 7919 + 1);
      var guard = 0;

      while (engine.phase == GamePhase.playing) {
        guard++;
        expect(guard, lessThan(300), reason: 'seed $seed: game never ended');

        // Enumerate all legal moves; the engine promises at least one exists
        // while phase == playing.
        final moves = <(int, int, int)>[];
        for (var i = 0; i < engine.tray.length; i++) {
          for (var y = 0; y < GridState.size; y++) {
            for (var x = 0; x < GridState.size; x++) {
              if (engine.tryPlaceWouldSucceed(i, x, y)) moves.add((i, x, y));
            }
          }
        }
        expect(moves, isNotEmpty,
            reason: 'seed $seed: playing phase but no legal move');

        final (i, x, y) = moves[rng.nextInt(moves.length)];
        final result = engine.tryPlace(i, x, y);
        expect(result, isNotNull, reason: 'seed $seed: legal move rejected');

        // Invariants after every move.
        expect(engine.tray.length, inInclusiveRange(1, 3),
            reason: 'seed $seed');
        expect(engine.movesUsed, lessThanOrEqualTo(40), reason: 'seed $seed');
        expect(engine.score, greaterThanOrEqualTo(0), reason: 'seed $seed');
      }

      // The score objective is unreachable, so every game ends stuck.
      expect(engine.phase, GamePhase.stuck, reason: 'seed $seed');
      expect(engine.stuckReason, isNotNull, reason: 'seed $seed');
    }
  });
}
