import 'package:flutter_test/flutter_test.dart';
import 'package:marianum_mobile/state/app/modules/app_modules.dart';
import 'package:marianum_mobile/storage/modules_settings.dart';

void main() {
  ModulesSettings settingsWith(List<Modules> order) =>
      ModulesSettings(moduleOrder: order, hiddenModules: []);

  group('effectiveModuleOrder', () {
    test('inserts modules missing from a stale persisted order at their '
        'default position', () {
      // Regression: persisted settings from before the ticker module existed
      // repeatedly made new modules vanish from bar, "Mehr" and settings list.
      final stale = Modules.values
          .where((m) => m != Modules.ticker)
          .toList();

      final effective = AppModule.effectiveModuleOrder(settingsWith(stale));

      expect(effective, Modules.values);
    });

    test('inserts a missing module after its closest present predecessor', () {
      final custom = [
        Modules.files,
        Modules.timetable,
        Modules.talk,
        Modules.marianumMessage,
      ];

      final effective = AppModule.effectiveModuleOrder(settingsWith(custom));

      expect(
        effective.indexOf(Modules.ticker),
        effective.indexOf(Modules.timetable) + 1,
      );
      expect(effective.toSet(), Modules.values.toSet());
    });

    test('keeps a complete persisted order untouched', () {
      final order = Modules.values.reversed.toList();
      expect(AppModule.effectiveModuleOrder(settingsWith(order)), order);
    });

    test('drops duplicates while preserving first occurrence', () {
      final order = [Modules.talk, Modules.timetable, Modules.talk];
      final effective = AppModule.effectiveModuleOrder(settingsWith(order));

      expect(effective.where((m) => m == Modules.talk), hasLength(1));
      expect(effective.first, Modules.talk);
      expect(effective.toSet(), Modules.values.toSet());
    });
  });

  group('reorderModuleOrder', () {
    test('moves within the displayed subset', () {
      final effective = AppModule.effectiveModuleOrder(settingsWith([]));

      final result = AppModule.reorderModuleOrder(
        displayed: effective,
        effective: effective,
        oldIndex: 0,
        newIndex: 2,
      );

      expect(result[2], effective[0]);
      expect(result.toSet(), effective.toSet());
    });

    test('non-displayed modules keep their slots', () {
      // Ticker is capability-filtered from the settings list; a reorder of the
      // visible modules must not move or drop it (previously the raw persisted
      // indices were used, moving the wrong module).
      final effective = AppModule.effectiveModuleOrder(settingsWith([]));
      final displayed = effective
          .where((m) => m != Modules.ticker)
          .toList();
      final tickerSlot = effective.indexOf(Modules.ticker);

      final result = AppModule.reorderModuleOrder(
        displayed: displayed,
        effective: effective,
        oldIndex: 0,
        newIndex: displayed.length - 1,
      );

      expect(result[tickerSlot], Modules.ticker);
      expect(result.toSet(), effective.toSet());
      expect(
        result.where((m) => m != Modules.ticker).toList(),
        displayed.sublist(1)..add(displayed.first),
      );
    });
  });
}
