// Tests for search ranking + aggregation (F8).

import 'package:flutter_test/flutter_test.dart';

import 'package:badnote/editor/search/search_ranking.dart';

void main() {
  group('scoreText', () {
    test('0 for empty query or no match', () {
      expect(scoreText('hello world', ''), 0);
      expect(scoreText('hello world', 'zzz'), 0);
      expect(scoreText('', 'x'), 0);
    });

    test('more occurrences score higher', () {
      final one = scoreText('cat dog', 'cat');
      final three = scoreText('cat cat cat', 'cat');
      expect(three, greaterThan(one));
    });

    test('earlier first match breaks ties (same count)', () {
      final early = scoreText('needle then padding padding', 'needle');
      final late = scoreText('padding padding then needle', 'needle');
      expect(early, greaterThan(late));
    });

    test('a match at position 0 gives earliness 1 (score = count + 1)', () {
      expect(scoreText('cat', 'cat'), closeTo(2.0, 1e-9)); // 1 occ + 1.0
    });
  });

  group('rankHits', () {
    test('drops non-matches and orders best-first', () {
      final hits = rankHits({
        'p1': 'one mention of fox',
        'p2': 'fox fox fox everywhere', // 3 occ → highest
        'p3': 'nothing relevant here',
      }, 'fox');
      expect(hits.map((h) => h.ref), ['p2', 'p1']);
      expect(hits.first.score, greaterThan(hits.last.score));
    });

    test('each hit carries a display snippet of the original text', () {
      final hits = rankHits({'p1': 'The quick brown fox jumps'}, 'fox');
      expect(hits, hasLength(1));
      expect(hits.first.snippet.match, 'fox');
      expect(hits.first.ref, 'p1');
    });

    test('equal scores keep input order (explicit tiebreak)', () {
      final hits = rankHits({
        'a': 'match',
        'b': 'match',
      }, 'match');
      expect(hits.map((h) => h.ref), ['a', 'b']);
      expect(hits[0].score, hits[1].score);
    });

    test('empty query → no hits', () {
      expect(rankHits({'a': 'anything'}, ''), isEmpty);
    });
  });
}
