import 'package:block_seasons/data/save_repository.dart';
import 'package:block_seasons/services/review_service.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';

class _FakeReviewer implements Reviewer {
  _FakeReviewer({this.available = true, this.throwOnRequest = false});

  bool available;
  bool throwOnRequest;
  int requestCalls = 0;

  @override
  Future<bool> isAvailable() async => available;

  @override
  Future<void> requestReview() async {
    requestCalls++;
    if (throwOnRequest) throw Exception('store unavailable');
  }
}

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  Future<SaveRepository> repoWithStagesCleared(int n) async {
    SharedPreferences.setMockInitialValues({});
    final repo = SaveRepository(await SharedPreferences.getInstance());
    for (var i = 0; i < n; i++) {
      await repo.recordResult(
          seasonId: 'season_001', stageId: 's$i', stars: 3, score: 100);
    }
    return repo;
  }

  test('requests a review and marks the flag on a qualifying win', () async {
    final repo = await repoWithStagesCleared(5);
    final reviewer = _FakeReviewer();
    final service = ReviewService(save: repo, reviewer: reviewer);

    await service.maybeRequestAfterWin(stars: 3);

    expect(reviewer.requestCalls, 1);
    expect(repo.reviewRequested, isTrue);
  });

  test('never requests twice', () async {
    final repo = await repoWithStagesCleared(5);
    await repo.markReviewRequested();
    final reviewer = _FakeReviewer();
    final service = ReviewService(save: repo, reviewer: reviewer);

    await service.maybeRequestAfterWin(stars: 3);

    expect(reviewer.requestCalls, 0);
  });

  test('does not request when the policy is not satisfied', () async {
    final repo = await repoWithStagesCleared(2); // below the 5-stage gate
    final reviewer = _FakeReviewer();
    final service = ReviewService(save: repo, reviewer: reviewer);

    await service.maybeRequestAfterWin(stars: 3);

    expect(reviewer.requestCalls, 0);
    expect(repo.reviewRequested, isFalse);
  });

  test('does not burn the one-shot when the store is unavailable', () async {
    final repo = await repoWithStagesCleared(5);
    final reviewer = _FakeReviewer(available: false);
    final service = ReviewService(save: repo, reviewer: reviewer);

    await service.maybeRequestAfterWin(stars: 3);

    expect(reviewer.requestCalls, 0);
    expect(repo.reviewRequested, isFalse, reason: 'retry on a later win');
  });

  test('swallows reviewer errors without throwing', () async {
    final repo = await repoWithStagesCleared(5);
    final reviewer = _FakeReviewer(throwOnRequest: true);
    final service = ReviewService(save: repo, reviewer: reviewer);

    // Must complete normally despite the plugin throwing.
    await service.maybeRequestAfterWin(stars: 3);
    expect(reviewer.requestCalls, 1);
  });
}
