// Copyright 2013 The Flutter Authors. 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:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
import 'package:flutter_test/flutter_test.dart';

import 'utils.dart';

void main() => defineTests();

void defineTests() {
  group(
    'structure',
    () {
      testWidgets(
        'footnote is detected and handle correctly',
        (WidgetTester tester) async {
          const String data = 'Foo[^a]\n[^a]: Bar';
          await tester.pumpWidget(
            boilerplate(
              const MarkdownBody(
                data: data,
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          expectTextStrings(widgets, <String>[
            'Foo1',
            '1.',
            'Bar ↩',
          ]);
        },
      );

      testWidgets(
        'footnote is detected and handle correctly for selectable markdown',
        (WidgetTester tester) async {
          const String data = 'Foo[^a]\n[^a]: Bar';
          await tester.pumpWidget(
            boilerplate(
              const MarkdownBody(
                data: data,
                selectable: true,
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          expectTextStrings(widgets, <String>[
            'Foo1',
            '1.',
            'Bar ↩',
          ]);
        },
      );

      testWidgets(
        'ignore footnotes without description',
        (WidgetTester tester) async {
          const String data = 'Foo[^1] Bar[^2]\n[^1]: Bar';
          await tester.pumpWidget(
            boilerplate(
              const MarkdownBody(
                data: data,
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          expectTextStrings(widgets, <String>[
            'Foo1 Bar[^2]',
            '1.',
            'Bar ↩',
          ]);
        },
      );
      testWidgets(
        'ignore superscripts and footnotes order',
        (WidgetTester tester) async {
          const String data = '[^2]: Bar \n [^1]: Foo \n Foo[^f] Bar[^b]';
          await tester.pumpWidget(
            boilerplate(
              const MarkdownBody(
                data: data,
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          expectTextStrings(widgets, <String>[
            'Foo1 Bar2',
            '1.',
            'Foo ↩',
            '2.',
            'Bar ↩',
          ]);
        },
      );

      testWidgets(
        'handle two digits superscript',
        (WidgetTester tester) async {
          const String data = '''
1[^1] 2[^2] 3[^3] 4[^4] 5[^5] 6[^6] 7[^7] 8[^8] 9[^9] 10[^10]
[^1]:1 
[^2]:2 
[^3]:3
[^4]:4
[^5]:5
[^6]:6
[^7]:7
[^8]:8
[^9]:9
[^10]:10
''';
          await tester.pumpWidget(
            boilerplate(
              const MarkdownBody(
                data: data,
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          expectTextStrings(widgets, <String>[
            '11 22 33 44 55 66 77 88 99 1010',
            '1.',
            '1 ↩',
            '2.',
            '2 ↩',
            '3.',
            '3 ↩',
            '4.',
            '4 ↩',
            '5.',
            '5 ↩',
            '6.',
            '6 ↩',
            '7.',
            '7 ↩',
            '8.',
            '8 ↩',
            '9.',
            '9 ↩',
            '10.',
            '10 ↩',
          ]);
        },
      );
    },
  );

  group(
    'superscript textstyle replacing',
    () {
      testWidgets(
        'superscript has correct default fontfeature',
        (WidgetTester tester) async {
          const String data = 'Foo[^a]\n[^a]: Bar';
          await tester.pumpWidget(
            boilerplate(
              const MarkdownBody(
                data: data,
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          final Text text = widgets.firstWhere((Widget widget) => widget is Text) as Text;

          final TextSpan span = text.textSpan! as TextSpan;
          final List<InlineSpan>? children = span.children;

          expect(children, isNotNull);
          expect(children!.length, 2);
          expect(children[1].style, isNotNull);
          expect(children[1].style!.fontFeatures?.length, 1);
          expect(children[1].style!.fontFeatures?.first.feature, 'sups');
        },
      );

      testWidgets(
        'superscript has correct custom fontfeature',
        (WidgetTester tester) async {
          const String data = 'Foo[^a]\n[^a]: Bar';
          await tester.pumpWidget(
            boilerplate(
              MarkdownBody(
                data: data,
                styleSheet: MarkdownStyleSheet(superscriptFontFeatureTag: 'numr'),
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          final Text text = widgets.firstWhere((Widget widget) => widget is Text) as Text;

          final TextSpan span = text.textSpan! as TextSpan;
          final List<InlineSpan>? children = span.children;

          expect(children, isNotNull);
          expect(children!.length, 2);
          expect(children[1].style, isNotNull);
          expect(children[1].style!.fontFeatures?.length, 2);
          expect(children[1].style!.fontFeatures?[1].feature, 'numr');
        },
      );

      testWidgets(
        'superscript index has the same font style like text',
        (WidgetTester tester) async {
          const String data = '# Foo[^a]\n[^a]: Bar';
          await tester.pumpWidget(
            boilerplate(
              const MarkdownBody(
                data: data,
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          final Text text = widgets.firstWhere((Widget widget) => widget is Text) as Text;

          final TextSpan span = text.textSpan! as TextSpan;
          final List<InlineSpan>? children = span.children;

          expect(children![0].style, isNotNull);
          expect(children[1].style!.fontSize, children[0].style!.fontSize);
          expect(children[1].style!.fontFamily, children[0].style!.fontFamily);
          expect(children[1].style!.fontStyle, children[0].style!.fontStyle);
          expect(children[1].style!.fontSize, children[0].style!.fontSize);
        },
      );

      testWidgets(
        'link is correctly copied to new superscript index',
        (WidgetTester tester) async {
          final List<MarkdownLink> linkTapResults = <MarkdownLink>[];
          const String data = 'Foo[^a]\n[^a]: Bar';
          await tester.pumpWidget(
            boilerplate(
              MarkdownBody(
                data: data,
                onTapLink: (String text, String? href, String title) =>
                    linkTapResults.add(MarkdownLink(text, href, title)),
              ),
            ),
          );

          final Iterable<Widget> widgets = tester.allWidgets;
          final Text text = widgets.firstWhere((Widget widget) => widget is Text) as Text;

          final TextSpan span = text.textSpan! as TextSpan;

          final List<Type> gestureRecognizerTypes = <Type>[];
          span.visitChildren((InlineSpan inlineSpan) {
            if (inlineSpan is TextSpan) {
              final TapGestureRecognizer? recognizer = inlineSpan.recognizer as TapGestureRecognizer?;
              gestureRecognizerTypes.add(recognizer?.runtimeType ?? Null);
              if (recognizer != null) {
                recognizer.onTap!();
              }
            }
            return true;
          });

          expect(span.children!.length, 2);
          expect(
            gestureRecognizerTypes,
            orderedEquals(<Type>[Null, TapGestureRecognizer]),
          );
          expectLinkTap(linkTapResults[0], const MarkdownLink('1', '#fn-a'));
        },
      );
    },
  );
}
