import 'package:commons_core/commons_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tane/services/message_store.dart';
import 'package:tane/services/unread_service.dart';
import 'package:tane/ui/unread_badge.dart';

import '../support/test_support.dart';

/// The badge is driven by [UnreadService]: it shows a count when there are
/// unread messages and clears when they're read — live, over the service's
/// `changes` stream. Injecting the service avoids the `getIt` lookup. The store
/// reads are real async, so mutations run inside `runAsync`.
void main() {
  late MessageStore store;
  late UnreadService unread;

  setUp(() {
    store = MessageStore(InMemorySecretStore());
    unread = UnreadService(store, InMemorySecretStore());
  });

  tearDown(() => unread.dispose());

  Widget host() => MaterialApp(
        home: Scaffold(
          body: UnreadBadge(
            peer: 'alice',
            service: unread,
            child: const Icon(Icons.chat_bubble),
          ),
        ),
      );

  Future<void> receive(int atMs) async {
    final m = PrivateMessage(
        fromPubkey: 'alice',
        text: 'hi',
        at: DateTime.fromMillisecondsSinceEpoch(atMs));
    await store.append('alice', m);
    await unread.onMessageReceived('alice', m);
  }

  // Lets pending async (store reads + the widget's stream-driven refresh)
  // resolve in the real zone, then renders a frame.
  Future<void> pumpRefresh(WidgetTester tester) async {
    for (var i = 0; i < 5; i++) {
      await tester.runAsync(
          () => Future<void>.delayed(const Duration(milliseconds: 20)));
      await tester.pump();
    }
  }

  testWidgets('shows an existing unread count on build', (tester) async {
    await tester.runAsync(() => receive(1000));
    await tester.pumpWidget(host());
    await pumpRefresh(tester);
    expect(find.text('1'), findsOneWidget);
  });

  testWidgets('reacts to a new message and to being read', (tester) async {
    await tester.pumpWidget(host());
    await pumpRefresh(tester);
    expect(find.text('1'), findsNothing);

    await tester.runAsync(() => receive(1000));
    await pumpRefresh(tester);
    expect(find.text('1'), findsOneWidget);

    await tester.runAsync(() => unread.markRead('alice'));
    await pumpRefresh(tester);
    expect(find.text('1'), findsNothing);
  });
}
