// Copyright (c) 2018, the Zefyr project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:test/test.dart';
import 'package:quill_delta/quill_delta.dart';
import 'package:notus/notus.dart';

final ul = NotusAttribute.ul.toJson();
final bold = NotusAttribute.bold.toJson();

void main() {
  group('$CatchAllInsertRule', () {
    final rule = new CatchAllInsertRule();

    test('applies change as-is', () {
      final doc = new Delta()..insert('Document\n');
      final actual = rule.apply(doc, 8, '!');
      final expected = new Delta()
        ..retain(8)
        ..insert('!');
      expect(actual, expected);
    });
  });

  group('$PreserveLineStyleOnSplitRule', () {
    final rule = new PreserveLineStyleOnSplitRule();

    test('skips at the beginning of a document', () {
      final doc = new Delta()..insert('One\n');
      final actual = rule.apply(doc, 0, '\n');
      expect(actual, isNull);
    });

    test('applies in a block', () {
      final doc = new Delta()
        ..insert('One and two')
        ..insert('\n', ul)
        ..insert('Three')
        ..insert('\n', ul);
      final actual = rule.apply(doc, 8, '\n');
      final expected = new Delta()
        ..retain(8)
        ..insert('\n', ul);
      expect(actual, isNotNull);
      expect(actual, expected);
    });
  });

  group('$ResetLineFormatOnNewLineRule', () {
    final rule = const ResetLineFormatOnNewLineRule();

    test('applies when line-break is inserted at the end of line', () {
      final doc = new Delta()
        ..insert('Hello world')
        ..insert('\n', NotusAttribute.h1.toJson());
      final actual = rule.apply(doc, 11, '\n');
      expect(actual, isNotNull);
      final expected = new Delta()
        ..retain(11)
        ..insert('\n', NotusAttribute.h1.toJson())
        ..retain(1, NotusAttribute.heading.unset.toJson());
      expect(actual, expected);
    });

    test('applies without style reset if not needed', () {
      final doc = new Delta()..insert('Hello world\n');
      final actual = rule.apply(doc, 11, '\n');
      expect(actual, isNotNull);
      final expected = new Delta()
        ..retain(11)
        ..insert('\n');
      expect(actual, expected);
    });

    test('applies at the beginning of a document', () {
      final doc = new Delta()
        ..insert('\n', NotusAttribute.h1.toJson());
      final actual = rule.apply(doc, 0, '\n');
      expect(actual, isNotNull);
      final expected = new Delta()
        ..insert('\n', NotusAttribute.h1.toJson())
        ..retain(1, NotusAttribute.heading.unset.toJson());
      expect(actual, expected);
    });

    test('applies and keeps block style', () {
      final style = NotusAttribute.ul.toJson();
      style.addAll(NotusAttribute.h1.toJson());
      final doc = new Delta()..insert('Hello world')..insert('\n', style);
      final actual = rule.apply(doc, 11, '\n');
      expect(actual, isNotNull);
      final expected = new Delta()
        ..retain(11)
        ..insert('\n', style)
        ..retain(1, NotusAttribute.heading.unset.toJson());
      expect(actual, expected);
    });

    test('applies to a line in the middle of a document', () {
      final doc = new Delta()
        ..insert('Hello \nworld!\nMore lines here.')
        ..insert('\n', NotusAttribute.h2.toJson());
      final actual = rule.apply(doc, 30, '\n');
      expect(actual, isNotNull);
      final expected = new Delta()
        ..retain(30)
        ..insert('\n', NotusAttribute.h2.toJson())
        ..retain(1, NotusAttribute.heading.unset.toJson());
      expect(actual, expected);
    });
  });

  group('$AutoExitBlockRule', () {
    final rule = new AutoExitBlockRule();

    test('applies when line-break is inserted on empty line in a block', () {
      final ul = NotusAttribute.ul.toJson();
      final doc = new Delta()
        ..insert('Item 1')
        ..insert('\n', ul)
        ..insert('Item 2')
        ..insert('\n\n', ul);
      final actual = rule.apply(doc, 14, '\n');
      expect(actual, isNotNull);
      final expected = new Delta()
        ..retain(14)
        ..retain(1, NotusAttribute.block.unset.toJson());
      expect(actual, expected);
    });

    test('applies only on empty line', () {
      final ul = NotusAttribute.ul.toJson();
      final doc = new Delta()..insert('Item 1')..insert('\n', ul);
      final actual = rule.apply(doc, 6, '\n');
      expect(actual, isNull);
    });

    test('applies at the beginning of a document', () {
      final ul = NotusAttribute.ul.toJson();
      final doc = new Delta()..insert('\n', ul);
      final actual = rule.apply(doc, 0, '\n');
      expect(actual, isNotNull);
      final expected = new Delta()
        ..retain(1, NotusAttribute.block.unset.toJson());
      expect(actual, expected);
    });

    test('ignores non-empty line at the beginning of a document', () {
      final ul = NotusAttribute.ul.toJson();
      final doc = new Delta()..insert('Text\n', ul);
      final actual = rule.apply(doc, 0, '\n');
      expect(actual, isNull);
    });
  });

  group('$PreserveInlineStylesRule', () {
    final rule = new PreserveInlineStylesRule();
    test('apply', () {
      final doc = new Delta()
        ..insert('Doc with ')
        ..insert('bold', bold)
        ..insert(' text');
      final actual = rule.apply(doc, 13, 'er');
      final expected = new Delta()
        ..retain(13)
        ..insert('er', bold);
      expect(expected, actual);
    });

    test('apply at the beginning of a document', () {
      final doc = new Delta()..insert('Doc with ');
      final actual = rule.apply(doc, 0, 'A ');
      expect(actual, isNull);
    });
  });

  group('$AutoFormatLinksRule', () {
    final rule = new AutoFormatLinksRule();
    final link = NotusAttribute.link.fromString('https://example.com').toJson();

    test('apply simple', () {
      final doc = new Delta()..insert('Doc with link https://example.com');
      final actual = rule.apply(doc, 33, ' ');
      final expected = new Delta()
        ..retain(14)
        ..retain(19, link)
        ..insert(' ');
      expect(expected, actual);
    });

    test('applies only to insert of single space', () {
      final doc = new Delta()..insert('Doc with link https://example.com');
      final actual = rule.apply(doc, 33, '/');
      expect(actual, isNull);
    });

    test('applies for links at the beginning of line', () {
      final doc = new Delta()..insert('Doc with link\nhttps://example.com');
      final actual = rule.apply(doc, 33, ' ');
      final expected = new Delta()
        ..retain(14)
        ..retain(19, link)
        ..insert(' ');
      expect(expected, actual);
    });

    test('ignores if already formatted as link', () {
      final doc = new Delta()
        ..insert('Doc with link\n')
        ..insert('https://example.com', link);
      final actual = rule.apply(doc, 33, ' ');
      expect(actual, isNull);
    });
  });
}
