import 'dart:async';

import 'package:flutter_test/flutter_test.dart';
import 'package:marianum_mobile/widget/async_action_button.dart';

void main() {
  group('AsyncActionController.run', () {
    test('toggles busy true while running and false after success', () async {
      final controller = AsyncActionController();
      addTearDown(controller.dispose);

      var seenBusyInsideCallback = false;
      final ok = await controller.run(() async {
        seenBusyInsideCallback = controller.busy;
      });

      expect(
        seenBusyInsideCallback,
        isTrue,
        reason: 'busy must be true while the callback is running',
      );
      expect(ok, isTrue);
      expect(controller.busy, isFalse);
      expect(controller.error, isNull);
    });

    test(
      'captures mapped error message on failure and returns false',
      () async {
        final controller = AsyncActionController();
        addTearDown(controller.dispose);

        final ok = await controller.run(
          () async => throw Exception('boom'),
          errorBuilder: (e) => 'custom: $e',
        );

        expect(ok, isFalse);
        expect(controller.busy, isFalse);
        expect(controller.error, contains('custom:'));
        expect(controller.error, contains('boom'));
      },
    );

    test('rejects re-entry while busy', () async {
      final controller = AsyncActionController();
      addTearDown(controller.dispose);

      final firstStarted = Completer<void>();
      final firstCanFinish = Completer<void>();
      final firstFuture = controller.run(() async {
        firstStarted.complete();
        await firstCanFinish.future;
      });

      await firstStarted.future;
      expect(controller.busy, isTrue);

      final reentrant = await controller.run(() async {});
      expect(
        reentrant,
        isFalse,
        reason:
            'second run while busy must be rejected without invoking callback',
      );

      firstCanFinish.complete();
      expect(await firstFuture, isTrue);
      expect(controller.busy, isFalse);
    });

    test('clearError resets error and notifies listeners', () async {
      final controller = AsyncActionController();
      addTearDown(controller.dispose);

      var notifyCount = 0;
      controller.addListener(() => notifyCount++);

      await controller.run(() async => throw Exception('x'));
      expect(controller.error, isNotNull);
      final beforeClear = notifyCount;

      controller.clearError();
      expect(controller.error, isNull);
      expect(notifyCount, beforeClear + 1);

      // No-op when already cleared.
      controller.clearError();
      expect(notifyCount, beforeClear + 1);
    });
  });
}
