import 'package:commons_core/commons_core.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tane/services/message_store.dart';
import 'package:tane/services/unread_service.dart';

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

/// Unread counting is derived from stored history plus a per-peer "last read"
/// mark; these tests drive it through a real [MessageStore] over an in-memory
/// keystore, so no relay or network is involved.
void main() {
  const me = 'me-pubkey';

  late MessageStore store;
  late UnreadService unread;

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

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

  PrivateMessage msg(String from, String text, int atMs) => PrivateMessage(
      fromPubkey: from,
      text: text,
      at: DateTime.fromMillisecondsSinceEpoch(atMs));

  // Persist then notify, the way InboxService.ingest does.
  Future<void> receive(String peer, PrivateMessage m) async {
    await store.append(peer, m);
    await unread.onMessageReceived(peer, m);
  }

  test('an unknown peer has no unread', () async {
    expect(await unread.unreadCount('alice'), 0);
  });

  test('inbound messages increment the unread count', () async {
    await receive('alice', msg('alice', 'hi', 1000));
    await receive('alice', msg('alice', 'you there?', 2000));
    expect(await unread.unreadCount('alice'), 2);
  });

  test('markRead clears older messages but keeps newer ones', () async {
    await receive('alice', msg('alice', 'a', 1000));
    await unread.markRead('alice'); // reads up to the latest (1000)
    expect(await unread.unreadCount('alice'), 0);

    await receive('alice', msg('alice', 'b', 2000));
    expect(await unread.unreadCount('alice'), 1);
  });

  test('own outgoing messages never count as unread', () async {
    await receive('alice', msg(me, 'my reply', 1000));
    expect(await unread.unreadCount('alice'), 0);
  });

  test('a message for the open chat is auto-read (no unread)', () async {
    unread.activePeer = 'alice';
    await receive('alice', msg('alice', 'while open', 1000));
    expect(await unread.unreadCount('alice'), 0);
  });

  test('totalUnreadCount sums across conversations', () async {
    await receive('alice', msg('alice', 'a', 1000));
    await receive('bob', msg('bob', 'b1', 1000));
    await receive('bob', msg('bob', 'b2', 2000));
    expect(await unread.totalUnreadCount(), 3);
  });

  test('changes fires on a new inbound message', () async {
    final events = <void>[];
    final sub = unread.changes.listen(events.add);
    await receive('alice', msg('alice', 'hi', 1000));
    await Future<void>.delayed(Duration.zero);
    expect(events, hasLength(1));
    await sub.cancel();
  });

  test('changes fires on markRead', () async {
    await receive('alice', msg('alice', 'hi', 1000));
    final events = <void>[];
    final sub = unread.changes.listen(events.add);
    await unread.markRead('alice');
    await Future<void>.delayed(Duration.zero);
    expect(events, hasLength(1));
    await sub.cancel();
  });
}
