// Copyright 2014 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/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';

import '../widgets/clipboard_utils.dart';
import '../widgets/editable_text_utils.dart';

void main() {
  final MockClipboard mockClipboard = MockClipboard();

  setUp(() async {
    TestWidgetsFlutterBinding.ensureInitialized();
    TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMethodCallHandler(
      SystemChannels.platform,
      mockClipboard.handleMethodCall,
    );
    // Fill the clipboard so that the Paste option is available in the text
    // selection menu.
    await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
  });

  testWidgets('Builds the right toolbar on each platform, including web, and shows buttonItems', (WidgetTester tester) async {
    const String buttonText = 'Click me';

    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          body: Center(
            child: AdaptiveTextSelectionToolbar.buttonItems(
              anchors: const TextSelectionToolbarAnchors(
                primaryAnchor: Offset.zero,
              ),
              buttonItems: <ContextMenuButtonItem>[
                ContextMenuButtonItem(
                  label: buttonText,
                  onPressed: () {
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );

    expect(find.text(buttonText), findsOneWidget);

    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        expect(find.byType(TextSelectionToolbar), findsOneWidget);
        expect(find.byType(CupertinoTextSelectionToolbar), findsNothing);
        expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
        expect(find.byType(CupertinoDesktopTextSelectionToolbar), findsNothing);
        break;
      case TargetPlatform.iOS:
        expect(find.byType(TextSelectionToolbar), findsNothing);
        expect(find.byType(CupertinoTextSelectionToolbar), findsOneWidget);
        expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
        expect(find.byType(CupertinoDesktopTextSelectionToolbar), findsNothing);
        break;
      case TargetPlatform.macOS:
        expect(find.byType(TextSelectionToolbar), findsNothing);
        expect(find.byType(CupertinoTextSelectionToolbar), findsNothing);
        expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
        expect(find.byType(CupertinoDesktopTextSelectionToolbar), findsOneWidget);
        break;
      case TargetPlatform.fuchsia:
      case TargetPlatform.linux:
      case TargetPlatform.windows:
        expect(find.byType(TextSelectionToolbar), findsNothing);
        expect(find.byType(CupertinoTextSelectionToolbar), findsNothing);
        expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
        expect(find.byType(CupertinoDesktopTextSelectionToolbar), findsNothing);
        break;
    }
  },
    variant: TargetPlatformVariant.all(),
    skip: isBrowser, // [intended] see https://github.com/flutter/flutter/issues/108382
  );

  testWidgets('Can build children directly as well', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();

    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          body: Center(
            child: AdaptiveTextSelectionToolbar(
              anchors: const TextSelectionToolbarAnchors(
                primaryAnchor: Offset.zero,
              ),
              children: <Widget>[
                Container(key: key),
              ],
            ),
          ),
        ),
      ),
    );

    expect(find.byKey(key), findsOneWidget);
  });

  testWidgets('Can build from EditableTextState', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          body: Center(
            child: SizedBox(
              width: 400,
              child: EditableText(
                controller: TextEditingController(),
                backgroundCursorColor: const Color(0xff00ffff),
                focusNode: FocusNode(),
                style: const TextStyle(),
                cursorColor: const Color(0xff00ffff),
                selectionControls: materialTextSelectionHandleControls,
                contextMenuBuilder: (
                  BuildContext context,
                  EditableTextState editableTextState,
                ) {
                  return AdaptiveTextSelectionToolbar.editableText(
                    key: key,
                    editableTextState: editableTextState,
                  );
                },
              ),
            ),
          ),
        ),
      ),
    );

    await tester.pump(); // Wait for autofocus to take effect.

    expect(find.byKey(key), findsNothing);

    // Long-press to bring up the context menu.
    final Finder textFinder = find.byType(EditableText);
    await tester.longPress(textFinder);
    tester.state<EditableTextState>(textFinder).showToolbar();
    await tester.pumpAndSettle();

    expect(find.byKey(key), findsOneWidget);
    expect(find.text('Copy'), findsNothing);
    expect(find.text('Cut'), findsNothing);
    expect(find.text('Select all'), findsNothing);
    expect(find.text('Paste'), findsOneWidget);

    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
        expect(find.byType(TextSelectionToolbarTextButton), findsOneWidget);
        break;
      case TargetPlatform.iOS:
        expect(find.byType(CupertinoTextSelectionToolbarButton), findsOneWidget);
        break;
      case TargetPlatform.linux:
      case TargetPlatform.windows:
        expect(find.byType(DesktopTextSelectionToolbarButton), findsOneWidget);
        break;
      case TargetPlatform.macOS:
        expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsOneWidget);
        break;
    }
  },
    skip: kIsWeb, // [intended] on web the browser handles the context menu.
    variant: TargetPlatformVariant.all(),
  );

  testWidgets('Can build for editable text from raw parameters', (WidgetTester tester) async {
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          body: Center(
            child: AdaptiveTextSelectionToolbar.editable(
              key: key,
              anchors: const TextSelectionToolbarAnchors(
                primaryAnchor: Offset.zero,
              ),
              clipboardStatus: ClipboardStatus.pasteable,
              onCopy: () {},
              onCut: () {},
              onPaste: () {},
              onSelectAll: () {},
            ),
          ),
        ),
      ),
    );

    expect(find.byKey(key), findsOneWidget);
    expect(find.text('Copy'), findsOneWidget);
    expect(find.text('Cut'), findsOneWidget);
    expect(find.text('Paste'), findsOneWidget);

    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
        expect(find.text('Select all'), findsOneWidget);
        expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(4));
        break;
      case TargetPlatform.iOS:
        expect(find.text('Select All'), findsOneWidget);
        expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(4));
        break;
      case TargetPlatform.linux:
      case TargetPlatform.windows:
        expect(find.text('Select all'), findsOneWidget);
        expect(find.byType(DesktopTextSelectionToolbarButton), findsNWidgets(4));
        break;
      case TargetPlatform.macOS:
        expect(find.text('Select All'), findsOneWidget);
        expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNWidgets(4));
        break;
    }
  },
    skip: kIsWeb, // [intended] on web the browser handles the context menu.
    variant: TargetPlatformVariant.all(),
  );

  group('buttonItems', () {
    testWidgets('getEditableTextButtonItems builds the correct button items per-platform', (WidgetTester tester) async {
      // Fill the clipboard so that the Paste option is available in the text
      // selection menu.
      await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));

      Set<ContextMenuButtonType> buttonTypes = <ContextMenuButtonType>{};
      final TextEditingController controller = TextEditingController();

      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: Center(
              child: EditableText(
                controller: controller,
                backgroundCursorColor: Colors.grey,
                focusNode: FocusNode(),
                style: const TextStyle(),
                cursorColor: Colors.red,
                selectionControls: materialTextSelectionHandleControls,
                contextMenuBuilder: (
                  BuildContext context,
                  EditableTextState editableTextState,
                ) {
                  buttonTypes = editableTextState.contextMenuButtonItems
                    .map((ContextMenuButtonItem buttonItem) => buttonItem.type)
                    .toSet();
                  return const SizedBox.shrink();
                },
              ),
            ),
          ),
        ),
      );

      final EditableTextState state =
          tester.state<EditableTextState>(find.byType(EditableText));

      // With no text in the field.
      await tester.tapAt(textOffsetToPosition(tester, 0));
      await tester.pump();
      expect(state.showToolbar(), true);
      await tester.pump();

      expect(buttonTypes, isNot(contains(ContextMenuButtonType.cut)));
      expect(buttonTypes, isNot(contains(ContextMenuButtonType.copy)));
      expect(buttonTypes, contains(ContextMenuButtonType.paste));
      expect(buttonTypes, isNot(contains(ContextMenuButtonType.selectAll)));

      // With text but no selection.
      controller.text = 'lorem ipsum';
      await tester.pump();

      expect(buttonTypes, isNot(contains(ContextMenuButtonType.cut)));
      expect(buttonTypes, isNot(contains(ContextMenuButtonType.copy)));
      expect(buttonTypes, contains(ContextMenuButtonType.paste));

      switch (defaultTargetPlatform) {
        case TargetPlatform.android:
        case TargetPlatform.iOS:
        case TargetPlatform.fuchsia:
        case TargetPlatform.linux:
        case TargetPlatform.windows:
          expect(buttonTypes, contains(ContextMenuButtonType.selectAll));
          break;
        case TargetPlatform.macOS:
          expect(buttonTypes, isNot(contains(ContextMenuButtonType.selectAll)));
          break;
      }

      // With text and selection.
      controller.value = controller.value.copyWith(
        selection: const TextSelection(
          baseOffset: 0,
          extentOffset: 'lorem'.length,
        ),
      );
      await tester.pump();

      expect(buttonTypes, contains(ContextMenuButtonType.cut));
      expect(buttonTypes, contains(ContextMenuButtonType.copy));
      expect(buttonTypes, contains(ContextMenuButtonType.paste));

      switch (defaultTargetPlatform) {
        case TargetPlatform.android:
        case TargetPlatform.fuchsia:
        case TargetPlatform.linux:
        case TargetPlatform.windows:
          expect(buttonTypes, contains(ContextMenuButtonType.selectAll));
          break;
        case TargetPlatform.iOS:
        case TargetPlatform.macOS:
          expect(buttonTypes, isNot(contains(ContextMenuButtonType.selectAll)));
          break;
      }
    },
      variant: TargetPlatformVariant.all(),
      skip: kIsWeb, // [intended]
    );

    testWidgets('getAdaptiveButtons builds the correct button widgets per-platform', (WidgetTester tester) async {
      const String buttonText = 'Click me';

      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: Center(
              child: Builder(
                builder: (BuildContext context) {
                  final List<ContextMenuButtonItem> buttonItems = <ContextMenuButtonItem>[
                    ContextMenuButtonItem(
                      label: buttonText,
                      onPressed: () {
                      },
                    ),
                  ];
                  return ListView(
                    children: AdaptiveTextSelectionToolbar.getAdaptiveButtons(
                      context,
                      buttonItems,
                    ).toList(),
                  );
                },
              ),
            ),
          ),
        ),
      );

      expect(find.text(buttonText), findsOneWidget);

      switch (defaultTargetPlatform) {
        case TargetPlatform.fuchsia:
        case TargetPlatform.android:
          expect(find.byType(TextSelectionToolbarTextButton), findsOneWidget);
          expect(find.byType(CupertinoTextSelectionToolbarButton), findsNothing);
          expect(find.byType(DesktopTextSelectionToolbarButton), findsNothing);
          expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNothing);
          break;
        case TargetPlatform.iOS:
          expect(find.byType(TextSelectionToolbarTextButton), findsNothing);
          expect(find.byType(CupertinoTextSelectionToolbarButton), findsOneWidget);
          expect(find.byType(DesktopTextSelectionToolbarButton), findsNothing);
          expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNothing);
          break;
        case TargetPlatform.macOS:
          expect(find.byType(TextSelectionToolbarTextButton), findsNothing);
          expect(find.byType(CupertinoTextSelectionToolbarButton), findsNothing);
          expect(find.byType(DesktopTextSelectionToolbarButton), findsNothing);
          expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsOneWidget);
          break;
        case TargetPlatform.linux:
        case TargetPlatform.windows:
          expect(find.byType(TextSelectionToolbarTextButton), findsNothing);
          expect(find.byType(CupertinoTextSelectionToolbarButton), findsNothing);
          expect(find.byType(DesktopTextSelectionToolbarButton), findsOneWidget);
          expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNothing);
          break;
      }
    },
      variant: TargetPlatformVariant.all(),
    );
  });
}
