import 'package:flutter_test/flutter_test.dart';
import 'package:matrix/matrix.dart';
import 'package:pyramid/features/spaces/space_admin_dialog.dart';

/// Fake-Client für `updatePowerLevelsSafely`: liefert einen vorgegebenen
/// Server-Stand des power_levels-Events und zeichnet auf, was (wenn
/// überhaupt) zurückgeschrieben wird. Alles andere → NoSuchMethodError.
class _FakeClient implements Client {
  _FakeClient({required this.serverState, this.getError});

  Map<String, Object?> serverState;
  MatrixException? getError;
  Map<String, Object?>? written;

  @override
  String? get userID => '@me:pyramid.example';

  @override
  Future<Map<String, Object?>> getRoomStateWithKey(
    String roomId,
    String eventType,
    String stateKey, {
    Format? format,
  }) async {
    final err = getError;
    if (err != null) throw err;
    return serverState;
  }

  @override
  Future<String> setRoomStateWithKey(
    String roomId,
    String eventType,
    String stateKey,
    Map<String, Object?> body,
  ) async {
    written = body;
    return r'$neuesEvent';
  }

  @override
  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

class _FakeRoom implements Room {
  _FakeRoom(this.client);

  @override
  final Client client;

  @override
  String get id => '!raum:pyramid.example';

  @override
  dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

void main() {
  test('Server-Stand bleibt erhalten: fremde Einträge werden nie überschrieben '
      '(genau der Bug, der schon einmal Admins ausgesperrt hat)', () async {
    final client = _FakeClient(serverState: {
      'users': {'@me:pyramid.example': 100, '@uta:pyramid.example': 50},
      'users_default': 0,
      'state_default': 50,
    });
    final room = _FakeRoom(client);

    await updatePowerLevelsSafely(room, (content) {
      (content['users'] as Map<String, dynamic>)['@neu:pyramid.example'] = 50;
    });

    final users = client.written!['users'] as Map;
    expect(users['@uta:pyramid.example'], 50,
        reason: 'bestehender Server-Eintrag muss überleben');
    expect(users['@me:pyramid.example'], 100);
    expect(users['@neu:pyramid.example'], 50);
  });

  test('Selbst-Degradierung unter das nötige Level wird abgebrochen, '
      'nichts wird geschrieben', () async {
    final client = _FakeClient(serverState: {
      'users': {'@me:pyramid.example': 100},
      'state_default': 50,
    });
    final room = _FakeRoom(client);

    await expectLater(
      updatePowerLevelsSafely(room, (content) {
        (content['users'] as Map<String, dynamic>)['@me:pyramid.example'] = 0;
      }),
      throwsException,
    );
    expect(client.written, isNull);
  });

  test('eigener Eintrag entfernt → Fallback auf users_default greift und '
      'schützt ebenfalls', () async {
    final client = _FakeClient(serverState: {
      'users': {'@me:pyramid.example': 100},
      'users_default': 0,
    });
    final room = _FakeRoom(client);

    await expectLater(
      updatePowerLevelsSafely(room, (content) {
        (content['users'] as Map<String, dynamic>)
            .remove('@me:pyramid.example');
      }),
      throwsException,
    );
    expect(client.written, isNull);
  });

  test('explizites events[m.room.power_levels]-Erfordernis wird respektiert',
      () async {
    final client = _FakeClient(serverState: {
      'users': {'@me:pyramid.example': 60},
      'events': {'m.room.power_levels': 75},
      'state_default': 50,
    });
    final room = _FakeRoom(client);

    // 60 < 75 → muss abbrechen, obwohl state_default (50) erfüllt wäre.
    await expectLater(
      updatePowerLevelsSafely(room, (_) {}),
      throwsException,
    );
    expect(client.written, isNull);
  });

  test('fehlendes power_levels-Event (M_NOT_FOUND): Start mit leerem Event, '
      'Schreiben nur wenn man sich selbst hoch genug setzt', () async {
    final client = _FakeClient(
      serverState: {},
      getError: MatrixException.fromJson(
          {'errcode': 'M_NOT_FOUND', 'error': 'Event not found.'}),
    );
    final room = _FakeRoom(client);

    // Ohne eigenen Eintrag: users_default 0 < state_default-Fallback 50 → Abbruch.
    await expectLater(
      updatePowerLevelsSafely(room, (_) {}),
      throwsException,
    );
    expect(client.written, isNull);

    // Setzt man sich selbst auf 100, darf geschrieben werden.
    await updatePowerLevelsSafely(room, (content) {
      (content['users'] as Map<String, dynamic>)['@me:pyramid.example'] = 100;
    });
    final users = client.written!['users'] as Map;
    expect(users['@me:pyramid.example'], 100);
  });

  test('andere Serverfehler (z. B. M_FORBIDDEN) werden durchgereicht, '
      'nichts wird geschrieben', () async {
    final client = _FakeClient(
      serverState: {},
      getError: MatrixException.fromJson(
          {'errcode': 'M_FORBIDDEN', 'error': 'Nope.'}),
    );
    final room = _FakeRoom(client);

    await expectLater(
      updatePowerLevelsSafely(room, (_) {}),
      throwsA(isA<MatrixException>()),
    );
    expect(client.written, isNull);
  });
}
