/*
 *   Famedly Matrix SDK
 *   Copyright (C) 2022 Famedly GmbH
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as
 *   published by the Free Software Foundation, either version 3 of the
 *   License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see .
 */
import 'dart:async';
import 'package:test/test.dart';
import 'package:matrix/matrix.dart';
import 'fake_client.dart';
void main() async {
  group('Timeline', () {
    Logs().level = Level.error;
    final insertList = [];
    late Client client;
    setUp(() async {
      client = await getClient(
        sendTimelineEventTimeout: const Duration(seconds: 5),
      );
      await client.abortSync();
      insertList.clear();
    });
    tearDown(() async => client.dispose().onError((e, s) {}));
    test('archive room not loaded', () async {
      final archiveRoom =
          client.getArchiveRoomFromCache('!5345234234:example.com');
      expect(archiveRoom, null);
    });
    test('get archive', () async {
      final archive = await client.loadArchiveWithTimeline();
      expect(archive.length, 2);
      expect(client.rooms.length, 3);
      expect(archive[0].room.id, '!5345234234:example.com');
      expect(archive[0].room.membership, Membership.leave);
      expect(archive[0].room.name, 'The room name');
      expect(
        archive[0].room.lastEvent?.body,
        'This is a second text example message',
      );
      expect(archive[0].room.roomAccountData.length, 1);
      expect(archive[1].room.id, '!5345234235:example.com');
      expect(archive[1].room.membership, Membership.leave);
      expect(archive[1].room.name, 'The room name 2');
      final archiveRoom =
          client.getArchiveRoomFromCache('!5345234234:example.com');
      expect(archiveRoom != null, true);
      expect(archiveRoom!.timeline.events.length, 2);
    });
    test('request history', () async {
      await client.loadArchiveWithTimeline();
      final archiveRoom = client.getRoomById('!5345234234:example.com');
      expect(archiveRoom != null, true);
      final timeline = await archiveRoom!.getTimeline(onInsert: insertList.add);
      expect(timeline.events.length, 2);
      expect(timeline.events[0].eventId, '143274597443PhrSn:example.org');
      expect(timeline.events[1].eventId, '143274597446PhrSn:example.org');
      await timeline.requestHistory();
      expect(timeline.events.length, 5);
      expect(timeline.events[0].eventId, '143274597443PhrSn:example.org');
      expect(timeline.events[1].eventId, '143274597446PhrSn:example.org');
      expect(timeline.events[2].eventId, '3143273582443PhrSn:example.org');
      expect(timeline.events[3].eventId, '2143273582443PhrSn:example.org');
      expect(timeline.events[4].eventId, '1143273582466PhrSn:example.org');
      expect(insertList.length, 3);
    });
    test('expect database to be empty', () async {
      await client.loadArchiveWithTimeline();
      final archiveRoom = client.getRoomById('!5345234234:example.com');
      expect(archiveRoom != null, true);
      final eventsFromStore = await client.database?.getEventList(
        archiveRoom!,
        start: 0,
        limit: Room.defaultHistoryCount,
      );
      expect(eventsFromStore?.isEmpty, true);
    });
    test('discard room from archives when membership change', () async {
      await client.loadArchiveWithTimeline();
      expect(
        client.getArchiveRoomFromCache('!5345234235:example.com') != null,
        true,
      );
      await client.handleSync(
        SyncUpdate(
          nextBatch: 't_456',
          rooms: RoomsUpdate(
            invite: {'!5345234235:example.com': InvitedRoomUpdate()},
          ),
        ),
      );
      expect(client.getArchiveRoomFromCache('!5345234235:example.com'), null);
    });
    test(
      "assert that key updates don't change membership",
      () async {
        const roomid = '!5345234235:example.com';
        // prep work to be able to set a last event that would trigger the (fixed) bug
        await client.loadArchiveWithTimeline();
        expect(client.getArchiveRoomFromCache(roomid) != null, true);
        expect(client.getRoomById(roomid)?.membership, Membership.leave);
        final outboundSession = await client.encryption?.keyManager
            .createOutboundGroupSession(roomid);
        final inboundSession =
            client.encryption!.keyManager.getInboundGroupSession(
          roomid,
          outboundSession!.outboundGroupSession!.session_id(),
        )!;
        // ensure encryption is "enabled"
        client.getRoomById(roomid)?.setState(
              StrippedStateEvent(
                type: EventTypes.Encryption,
                content: {'algorithm': AlgorithmTypes.megolmV1AesSha2},
                senderId: client.userID!,
                stateKey: '',
              ),
            );
        final encryptedEvent =
            await client.encryption!.encryptGroupMessagePayload(
          roomid,
          {'msgtype': 'm.room.text', 'body': 'empty'},
        );
        // reset client
        await client.dispose().onError((e, s) {});
        client = await getClient(
          sendTimelineEventTimeout: const Duration(seconds: 5),
        );
        await client.abortSync();
        insertList.clear();
        // now do our tests
        await client.loadArchiveWithTimeline();
        expect(client.getArchiveRoomFromCache(roomid) != null, true);
        expect(client.getRoomById(roomid)?.membership, Membership.leave);
        // set the last event
        final room = client.getRoomById(roomid)!;
        room.lastEvent = Event(
          type: EventTypes.Encrypted,
          content: encryptedEvent,
          senderId: client.userID!,
          eventId: '\$archivedencr',
          room: room,
          originServerTs: DateTime.now(),
        );
        // import the inbound session
        await client.encryption!.keyManager.setInboundGroupSession(
          roomid,
          inboundSession.sessionId,
          inboundSession.senderKey,
          inboundSession.content,
        );
        expect(client.getArchiveRoomFromCache(roomid) != null, true);
        expect(client.getRoomById(roomid)?.membership, Membership.leave);
      },
      tags: 'olm',
    );
    test('clear archive', () async {
      await client.loadArchiveWithTimeline();
      client.clearArchivesFromCache();
      expect(client.getArchiveRoomFromCache('!5345234234:example.com'), null);
    });
    test('logout', () async {
      await client.logout();
    });
  });
}