import 'package:flutter_test/flutter_test.dart';
import 'package:relatica/models/connection.dart';
import 'package:relatica/models/filters/string_filter.dart';
import 'package:relatica/models/filters/timeline_entry_filter.dart';
import 'package:relatica/models/timeline_entry.dart';
import 'package:relatica/utils/filter_runner.dart';

void main() {
  final entries = [
    TimelineEntry(
      body: 'Hello world',
      authorId: '1',
      tags: ['greeting'],
      externalLink: 'http://mastodon.social/@user1/1234',
    ),
    TimelineEntry(
        body: 'Goodbye',
        authorId: '1',
        tags: ['SendOff'],
        externalLink: 'http://mastodon.social/@user1/4567'),
    TimelineEntry(
      body: 'Lorem ipsum',
      authorId: '1',
      tags: ['latin'],
      externalLink: 'http://mastodon.social/@user1/7890',
    ),
    TimelineEntry(
      body: 'Hello world',
      authorId: '2',
      tags: ['greeting'],
      externalLink: 'http://trolltodon.social/@user2/12',
    ),
    TimelineEntry(
      body: 'Goodbye',
      authorId: '2',
      tags: ['SendOff'],
      externalLink: 'http://trolltodon.social/@user2/34',
    ),
    TimelineEntry(
      body: 'Lorem ipsum',
      authorId: '2',
      tags: ['LATIN'],
      externalLink: 'http://trolltodon.social/@user2/56',
    ),
    TimelineEntry(
      body: 'Chao',
      authorId: '2',
      tags: ['sendoff'],
      externalLink: 'http://trolltodon.social/@user2/78',
    ),
  ];

  group('Test StringFilter', () {
    test('Test equals', () {
      const filter = StringFilter(
        filterString: 'hello',
        type: ComparisonType.equals,
      );
      expect(filter.isFiltered('hello'), equals(true));
      expect(filter.isFiltered('Hello'), equals(false));
      expect(filter.isFiltered('hello!'), equals(false));
      expect(filter.isFiltered('help'), equals(false));
    });
    test('Test equalsIgnoreCase', () {
      const filter = StringFilter(
        filterString: 'hello',
        type: ComparisonType.equalsIgnoreCase,
      );
      expect(filter.isFiltered('hello'), equals(true));
      expect(filter.isFiltered('Hello'), equals(true));
      expect(filter.isFiltered('hello!'), equals(false));
      expect(filter.isFiltered('help'), equals(false));
    });
    test('Test endsWithIgnoresCase', () {
      const filter = StringFilter(
        filterString: 'world',
        type: ComparisonType.endsWithIgnoreCase,
      );
      expect(filter.isFiltered('world'), equals(true));
      expect(filter.isFiltered('hello WORld'), equals(true));
      expect(filter.isFiltered('worldwide'), equals(false));
      expect(filter.isFiltered('hello world!'), equals(false));
    });
    test('Test contains', () {
      const filter = StringFilter(
        filterString: 'hello',
        type: ComparisonType.contains,
      );
      expect(filter.isFiltered('hello world'), equals(true));
      expect(filter.isFiltered('Hello World'), equals(false));
      expect(filter.isFiltered('hello world'), equals(true));
      expect(filter.isFiltered('help'), equals(false));
    });
    test('Test containsIgnoreCase', () {
      const filter = StringFilter(
        filterString: 'hello',
        type: ComparisonType.containsIgnoreCase,
      );
      expect(filter.isFiltered('hello world'), equals(true));
      expect(filter.isFiltered('Hello World'), equals(true));
      expect(filter.isFiltered('hello world'), equals(true));
      expect(filter.isFiltered('Standard greeting #HelloWorld'), equals(true));
      expect(filter.isFiltered('help'), equals(false));
    });
  });

  group('Test TimelineEntryFilter', () {
    test('Empty Filter', () {
      final filter = TimelineEntryFilter.create(
        enabled: true,
        action: TimelineEntryFilterAction.hide,
        name: 'filter',
      );
      final expected = [false, false, false, false, false, false, false];
      final actual = entries.map((e) => filter.isFiltered(e)).toList();
      expect(actual, equals(expected));
    });
    test('Test Keyword Filter', () {
      final filter = TimelineEntryFilter.create(
        enabled: true,
        action: TimelineEntryFilterAction.hide,
        name: 'filter',
        keywords: ['hello', 'good'],
      );
      final expected = [true, true, false, true, true, false, false];
      final actual = entries.map((e) => filter.isFiltered(e)).toList();
      expect(actual, equals(expected));
    });

    test('Test Author Filter', () {
      final filter = TimelineEntryFilter.create(
        enabled: true,
        action: TimelineEntryFilterAction.hide,
        name: 'filter',
        authors: [Connection(id: '2')],
      );
      final expected = [false, false, false, true, true, true, true];
      final actual = entries.map((e) => filter.isFiltered(e)).toList();
      expect(actual, equals(expected));
    });

    group('Test Domain Filter', () {
      test('Exact match', () {
        final filter = TimelineEntryFilter.create(
          enabled: true,
          action: TimelineEntryFilterAction.hide,
          name: 'filter',
          domains: ['trolltodon.social'],
        );
        final expected = [false, false, false, true, true, true, true];
        final actual = entries.map((e) => filter.isFiltered(e)).toList();
        expect(actual, equals(expected));
      });

      test('Start wildcard', () {
        final filter = TimelineEntryFilter.create(
          enabled: true,
          action: TimelineEntryFilterAction.hide,
          name: 'filter',
          domains: ['*odon.social'],
        );
        final expected = [true, true, true, true, true, true, true];
        final actual = entries.map((e) => filter.isFiltered(e)).toList();
        expect(actual, equals(expected));
      });
    });

    test('Test Tag Filter', () {
      final filter = TimelineEntryFilter.create(
        enabled: true,
        action: TimelineEntryFilterAction.hide,
        name: 'filter',
        hashtags: ['latin', 'greet'],
      );
      final expected = [false, false, true, false, false, true, false];
      final actual = entries.map((e) => filter.isFiltered(e)).toList();
      expect(actual, equals(expected));
    });

    test('Test Author plus Keyword', () {
      final filter = TimelineEntryFilter.create(
        enabled: true,
        action: TimelineEntryFilterAction.hide,
        name: 'filter',
        authors: [Connection(id: '2')],
        keywords: ['good'],
      );
      final expected = [false, false, false, false, true, false, false];
      final actual = entries.map((e) => filter.isFiltered(e)).toList();
      expect(actual, equals(expected));
    });

    test('Test Author plus tag', () {
      final filter = TimelineEntryFilter.create(
        action: TimelineEntryFilterAction.hide,
        enabled: true,
        name: 'filter',
        authors: [Connection(id: '2')],
        hashtags: ['latin', 'greet'],
      );
      final expected = [false, false, false, false, false, true, false];
      final actual = entries.map((e) => filter.isFiltered(e)).toList();
      expect(actual, equals(expected));
    });

    test('Test Keyword plus tag', () {
      final filter = TimelineEntryFilter.create(
        action: TimelineEntryFilterAction.hide,
        enabled: true,
        name: 'filter',
        keywords: ['chao'],
        hashtags: ['SENDOFF'],
      );
      final expected = [false, false, false, false, false, false, true];
      final actual = entries.map((e) => filter.isFiltered(e)).toList();
      expect(actual, equals(expected));
    });

    test('Test all', () {
      final filter1 = TimelineEntryFilter.create(
        action: TimelineEntryFilterAction.hide,
        enabled: true,
        name: 'filter',
        authors: [Connection(id: '2'), Connection(id: '3')],
        keywords: ['chao'],
        hashtags: ['SENDOFF'],
      );
      final expected1 = [false, false, false, false, false, false, true];
      final actual1 = entries.map((e) => filter1.isFiltered(e)).toList();
      expect(actual1, equals(expected1));

      final filter2 = TimelineEntryFilter.create(
        action: TimelineEntryFilterAction.hide,
        enabled: true,
        name: 'filter',
        authors: [Connection(id: '1'), Connection(id: '3')],
        keywords: ['chao'],
        hashtags: ['SENDOFF'],
      );
      final expected2 = [false, false, false, false, false, false, false];
      final actual2 = entries.map((e) => filter2.isFiltered(e)).toList();
      expect(actual2, equals(expected2));
    });
  });

  test('Test runner', () {
    final runnerEntries = [
      ...entries,
      TimelineEntry(body: 'User 3 Post #1', authorId: '3'),
    ];
    final filters = [
      TimelineEntryFilter.create(
        action: TimelineEntryFilterAction.warn,
        enabled: true,
        name: 'send-off-hide-filter',
        hashtags: ['SENDOFF'],
      ),
      TimelineEntryFilter.create(
        action: TimelineEntryFilterAction.hide,
        enabled: true,
        name: 'author-3-hide',
        authors: [Connection(id: '3')],
      ),
      TimelineEntryFilter.create(
        action: TimelineEntryFilterAction.hide,
        enabled: true,
        name: 'send-off-hide-filter',
        authors: [Connection(id: '1')],
        hashtags: ['SENDOFF'],
      )
    ];

    final expected = [
      'show',
      'hide',
      'show',
      'show',
      'warn',
      'show',
      'warn',
      'hide',
    ];
    final actual = runnerEntries
        .map((e) => runFilters(e, filters).toActionString())
        .toList();
    expect(expected, equals(actual));
  });
}
