// Tests for the 双链 (bidirectional link) pure core (F7): [[link]] parsing +
// forward/backlink indices + dangling-link detection.

import 'package:flutter_test/flutter_test.dart';

import 'package:badnote/editor/link/link_graph.dart';

void main() {
  group('parseLinkTargets', () {
    test('extracts bracketed targets, trimmed', () {
      expect(parseLinkTargets('see [[Alpha]] and [[ Beta ]]'),
          ['Alpha', 'Beta']);
    });

    test('de-duplicates, keeping first-occurrence order', () {
      expect(parseLinkTargets('[[A]] [[B]] [[A]]'), ['A', 'B']);
    });

    test('ignores empty / whitespace-only targets', () {
      expect(parseLinkTargets('x [[]] y [[   ]] z [[Real]]'), ['Real']);
    });

    test('no links → empty', () {
      expect(parseLinkTargets('plain text, no links'), isEmpty);
    });
  });

  group('LinkGraph', () {
    test('backlinksOf collects every node linking to a target', () {
      final g = LinkGraph.fromTexts({
        'n1': 'links to [[n2]] and [[n3]]',
        'n2': 'links to [[n3]]',
        'n3': 'a leaf',
      });
      expect(g.linksFrom('n1'), {'n2', 'n3'});
      expect(g.linksFrom('n3'), isEmpty);
      expect(g.backlinksOf('n3'), {'n1', 'n2'});
      expect(g.backlinksOf('n2'), {'n1'});
      expect(g.backlinksOf('n1'), isEmpty);
    });

    test('self-links are ignored', () {
      final g = LinkGraph.fromTexts({'n1': 'I reference [[n1]] myself'});
      expect(g.linksFrom('n1'), isEmpty);
      expect(g.backlinksOf('n1'), isEmpty);
    });

    test('fromLinks builds the same indices from explicit targets', () {
      final g = LinkGraph.fromLinks({
        'a': ['b', 'c'],
        'b': ['c'],
        'c': [],
      });
      expect(g.backlinksOf('c'), {'a', 'b'});
      expect(g.linksFrom('a'), {'b', 'c'});
    });

    test('danglingTargets surfaces links to unknown nodes', () {
      final g = LinkGraph.fromLinks({
        'a': ['b', 'ghost'],
        'b': [],
      });
      expect(g.danglingTargets(), {'ghost'});
    });

    test('returned sets are unmodifiable', () {
      final g = LinkGraph.fromLinks({
        'a': ['b'],
        'b': [],
      });
      expect(() => g.linksFrom('a').add('x'), throwsUnsupportedError);
      expect(() => g.backlinksOf('b').add('x'), throwsUnsupportedError);
    });

    test('unknown node ids yield empty (no crash)', () {
      final g = LinkGraph.fromLinks({'a': []});
      expect(g.linksFrom('nope'), isEmpty);
      expect(g.backlinksOf('nope'), isEmpty);
    });
  });
}
