import 'package:flutter_test/flutter_test.dart';
import 'package:marianum_mobile/api/marianumcloud/webdav/queries/list_files/cacheable_file.dart';
import 'package:marianum_mobile/api/marianumcloud/webdav/queries/list_files/list_files_response.dart';
import 'package:marianum_mobile/view/pages/files/data/sort_options.dart';

CacheableFile _file({
  required String name,
  bool isDirectory = false,
  int? size,
  DateTime? modifiedAt,
}) => CacheableFile(
  path: '/$name',
  isDirectory: isDirectory,
  name: name,
  size: size,
  modifiedAt: modifiedAt,
);

void main() {
  group('SortOptions.options', () {
    test('name comparator is alphabetic', () {
      final cmp = SortOptions.getOption(SortOption.name).compare;
      expect(cmp(_file(name: 'a'), _file(name: 'b')), lessThan(0));
      expect(cmp(_file(name: 'b'), _file(name: 'a')), greaterThan(0));
      expect(cmp(_file(name: 'a'), _file(name: 'a')), 0);
    });

    test('date comparator is chronological by modifiedAt', () {
      final cmp = SortOptions.getOption(SortOption.date).compare;
      final older = _file(name: 'a', modifiedAt: DateTime(2026, 1, 1));
      final newer = _file(name: 'b', modifiedAt: DateTime(2026, 5, 1));
      expect(cmp(older, newer), lessThan(0));
      expect(cmp(newer, older), greaterThan(0));
    });

    test('size comparator pushes directories to the end (positional 1 vs 0)', () {
      final cmp = SortOptions.getOption(SortOption.size).compare;
      final dir = _file(name: 'd', isDirectory: true);
      final file = _file(name: 'f', size: 100);
      // (dir, file) → returns 1 (dir.isDirectory true) → file sorts before dir.
      expect(cmp(dir, file), 1);
      expect(cmp(file, dir), 0);
    });

    test('size comparator handles null sizes', () {
      final cmp = SortOptions.getOption(SortOption.size).compare;
      final noSize = _file(name: 'a');
      final withSize = _file(name: 'b', size: 100);
      // a.size == null → returns 0
      expect(cmp(noSize, withSize), 0);
      // b.size == null → returns 1
      expect(cmp(withSize, noSize), 1);
    });

    test('size comparator orders by file size when both known', () {
      final cmp = SortOptions.getOption(SortOption.size).compare;
      expect(
        cmp(_file(name: 'a', size: 100), _file(name: 'b', size: 200)),
        lessThan(0),
      );
      expect(
        cmp(_file(name: 'a', size: 200), _file(name: 'b', size: 100)),
        greaterThan(0),
      );
    });

    test('options map contains all enum values exactly once', () {
      expect(SortOptions.options.keys.toSet(), SortOption.values.toSet());
    });
  });

  group('ListFilesResponse.sortBy', () {
    final folderA = _file(name: 'A', isDirectory: true);
    final folderB = _file(name: 'B', isDirectory: true);
    final fileA = _file(
      name: 'aaa',
      size: 100,
      modifiedAt: DateTime(2026, 1, 1),
    );
    final fileB = _file(
      name: 'bbb',
      size: 50,
      modifiedAt: DateTime(2026, 5, 1),
    );

    // Note: sortBy uses a string-buffer sort + compareTo descending. The actual
    // list ordering reflects what users see in the file list.
    test('foldersToTop=true keeps folders before files regardless of name', () {
      final response = ListFilesResponse({fileA, fileB, folderA, folderB});
      final sorted = response.sortBy(sortOption: SortOption.name);
      final folderCount = sorted.takeWhile((f) => f.isDirectory).length;
      expect(folderCount, 2, reason: 'both folders should sit at the top');
    });

    test('foldersToTop=false intermixes folders and files', () {
      final response = ListFilesResponse({fileA, fileB, folderA, folderB});
      final sorted = response.sortBy(
        sortOption: SortOption.name,
        foldersToTop: false,
      );
      final folderPositions = <int>[];
      for (var i = 0; i < sorted.length; i++) {
        if (sorted[i].isDirectory) folderPositions.add(i);
      }
      // Without foldersToTop, folders aren't guaranteed to be at the front:
      // assert at least one folder is somewhere other than the very top of
      // a folders-first ordering.
      expect(folderPositions, isNot([0, 1]));
    });

    test('reversed flips the order within each section', () {
      final response = ListFilesResponse({fileA, fileB});
      final asc = response.sortBy(
        sortOption: SortOption.name,
        foldersToTop: false,
      );
      final desc = response.sortBy(
        sortOption: SortOption.name,
        foldersToTop: false,
        reversed: true,
      );
      expect(desc, asc.reversed.toList());
    });

    test('empty input yields an empty list', () {
      final response = ListFilesResponse({});
      expect(response.sortBy(), isEmpty);
    });
  });
}
