import 'dart:convert';

import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
import 'package:appflowy/plugins/document/presentation/banner.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
import 'package:appflowy/shared/appflowy_network_image.dart';
import 'package:appflowy/shared/appflowy_network_svg.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_item.dart';
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart';
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_svg/flowy_svg.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:string_validator/string_validator.dart';
import 'package:universal_platform/universal_platform.dart';

import 'util.dart';

// const String readme = 'Read me';
const String gettingStarted = 'Getting started';

extension Expectation on WidgetTester {
  /// Expect to see the home page and with a default read me page.
  Future<void> expectToSeeHomePageWithGetStartedPage() async {
    if (UniversalPlatform.isDesktopOrWeb) {
      final finder = find.byType(HomeStack);
      await pumpUntilFound(finder);
      expect(finder, findsOneWidget);
    } else if (UniversalPlatform.isMobile) {
      final finder = find.byType(MobileHomePage);
      await pumpUntilFound(finder);
      expect(finder, findsOneWidget);
    }

    final docFinder = find.textContaining(gettingStarted);
    await pumpUntilFound(docFinder);
  }

  Future<void> expectToSeeHomePage() async {
    final finder = find.byType(HomeStack);
    await pumpUntilFound(finder);
    expect(finder, findsOneWidget);
  }

  /// Expect to see the page name on the home page.
  void expectToSeePageName(
    String name, {
    String? parentName,
    ViewLayoutPB layout = ViewLayoutPB.Document,
    ViewLayoutPB parentLayout = ViewLayoutPB.Document,
  }) {
    final pageName = findPageName(
      name,
      layout: layout,
      parentName: parentName,
      parentLayout: parentLayout,
    );
    expect(pageName, findsOneWidget);
  }

  /// Expect not to see the page name on the home page.
  void expectNotToSeePageName(
    String name, {
    String? parentName,
    ViewLayoutPB layout = ViewLayoutPB.Document,
    ViewLayoutPB parentLayout = ViewLayoutPB.Document,
  }) {
    final pageName = findPageName(
      name,
      layout: layout,
      parentName: parentName,
      parentLayout: parentLayout,
    );
    expect(pageName, findsNothing);
  }

  /// Expect to see the document banner.
  void expectToSeeDocumentBanner() {
    expect(find.byType(DocumentBanner), findsOneWidget);
  }

  /// Expect not to see the document banner.
  void expectNotToSeeDocumentBanner() {
    expect(find.byType(DocumentBanner), findsNothing);
  }

  /// Expect to the markdown file export success dialog.
  void expectToExportSuccess() {
    final exportSuccess = find.byWidgetPredicate(
      (widget) =>
          widget is FlowyText &&
          widget.text == LocaleKeys.settings_files_exportFileSuccess.tr(),
    );
    expect(exportSuccess, findsOneWidget);
  }

  /// Expect to see the document header toolbar empty
  void expectToSeeEmptyDocumentHeaderToolbar() {
    final addCover = find.textContaining(
      LocaleKeys.document_plugins_cover_addCover.tr(),
    );
    final addIcon = find.textContaining(
      LocaleKeys.document_plugins_cover_addIcon.tr(),
    );
    expect(addCover, findsNothing);
    expect(addIcon, findsNothing);
  }

  void expectToSeeDocumentIcon(String? emoji) {
    if (emoji == null) {
      final iconWidget = find.byType(EmojiIconWidget);
      expect(iconWidget, findsNothing);
      return;
    }
    final iconWidget = find.byWidgetPredicate(
      (widget) => widget is EmojiIconWidget && widget.emoji.emoji == emoji,
    );
    expect(iconWidget, findsOneWidget);
  }

  void expectDocumentIconNotNull() {
    final iconWidget = find.byWidgetPredicate(
      (widget) => widget is EmojiIconWidget && widget.emoji.isNotEmpty,
    );
    expect(iconWidget, findsOneWidget);
  }

  void expectToSeeDocumentCover(CoverType type) {
    final findCover = find.byWidgetPredicate(
      (widget) => widget is DocumentCover && widget.coverType == type,
    );
    expect(findCover, findsOneWidget);
  }

  void expectToSeeNoDocumentCover() {
    final findCover = find.byType(DocumentCover);
    expect(findCover, findsNothing);
  }

  void expectChangeCoverAndDeleteButton() {
    final findChangeCover = find.text(
      LocaleKeys.document_plugins_cover_changeCover.tr(),
    );
    final findRemoveIcon = find.byType(DeleteCoverButton);
    expect(findChangeCover, findsOneWidget);
    expect(findRemoveIcon, findsOneWidget);
  }

  /// Expect to see a text
  void expectToSeeText(String text) {
    Finder textWidget = find.textContaining(text, findRichText: true);
    if (textWidget.evaluate().isEmpty) {
      textWidget = find.byWidgetPredicate(
        (widget) => widget is FlowyText && widget.text == text,
      );
    }
    expect(textWidget, findsOneWidget);
  }

  /// Find if the page is favorite
  Finder findFavoritePageName(
    String name, {
    ViewLayoutPB layout = ViewLayoutPB.Document,
    String? parentName,
    ViewLayoutPB parentLayout = ViewLayoutPB.Document,
  }) =>
      find.byWidgetPredicate(
        (widget) =>
            widget is SingleInnerViewItem &&
            widget.view.isFavorite &&
            widget.spaceType == FolderSpaceType.favorite &&
            widget.view.name == name &&
            widget.view.layout == layout,
        skipOffstage: false,
      );

  Finder findAllFavoritePages() => find.byWidgetPredicate(
        (widget) =>
            widget is SingleInnerViewItem &&
            widget.view.isFavorite &&
            widget.spaceType == FolderSpaceType.favorite,
      );

  Finder findPageName(
    String name, {
    ViewLayoutPB layout = ViewLayoutPB.Document,
    String? parentName,
    ViewLayoutPB parentLayout = ViewLayoutPB.Document,
  }) {
    if (UniversalPlatform.isDesktop) {
      if (parentName == null) {
        return find.byWidgetPredicate(
          (widget) =>
              widget is SingleInnerViewItem &&
              widget.view.name == name &&
              widget.view.layout == layout,
          skipOffstage: false,
        );
      }

      return find.descendant(
        of: find.byWidgetPredicate(
          (widget) =>
              widget is InnerViewItem &&
              widget.view.name == parentName &&
              widget.view.layout == parentLayout,
          skipOffstage: false,
        ),
        matching: findPageName(name, layout: layout),
      );
    }

    return find.byWidgetPredicate(
      (widget) =>
          widget is SingleMobileInnerViewItem &&
          widget.view.name == name &&
          widget.view.layout == layout,
      skipOffstage: false,
    );
  }

  void expectViewHasIcon(String name, ViewLayoutPB layout, EmojiIconData data) {
    final pageName = findPageName(
      name,
      layout: layout,
    );
    final type = data.type;
    if (type == FlowyIconType.emoji) {
      final icon = find.descendant(
        of: pageName,
        matching: find.text(data.emoji),
      );
      expect(icon, findsOneWidget);
    } else if (type == FlowyIconType.icon) {
      final iconsData = IconsData.fromJson(jsonDecode(data.emoji));
      final icon = find.descendant(
        of: pageName,
        matching: find.byWidgetPredicate(
          (w) => w is FlowySvg && w.svgString == iconsData.svgString,
        ),
      );
      expect(icon, findsOneWidget);
    } else if (type == FlowyIconType.custom) {
      final isSvg = data.emoji.endsWith('.svg');
      if (isURL(data.emoji)) {
        final image = find.descendant(
          of: pageName,
          matching: isSvg
              ? find.byType(FlowyNetworkSvg)
              : find.byType(FlowyNetworkImage),
        );
        expect(image, findsOneWidget);
      } else {
        final image = find.descendant(
          of: pageName,
          matching: isSvg ? find.byType(SvgPicture) : find.byType(Image),
        );
        expect(image, findsOneWidget);
      }
    }
  }

  void expectViewTitleHasIcon(
    String name,
    ViewLayoutPB layout,
    EmojiIconData data,
  ) {
    final type = data.type;
    if (type == FlowyIconType.emoji) {
      final icon = find.descendant(
        of: find.byType(ViewTitleBar),
        matching: find.text(data.emoji),
      );
      expect(icon, findsOneWidget);
    } else if (type == FlowyIconType.icon) {
      final iconsData = IconsData.fromJson(jsonDecode(data.emoji));
      final icon = find.descendant(
        of: find.byType(ViewTitleBar),
        matching: find.byWidgetPredicate(
          (w) => w is FlowySvg && w.svgString == iconsData.svgString,
        ),
      );
      expect(icon, findsOneWidget);
    } else if (type == FlowyIconType.custom) {
      final isSvg = data.emoji.endsWith('.svg');
      if (isURL(data.emoji)) {
        final image = find.descendant(
          of: find.byType(ViewTitleBar),
          matching: isSvg
              ? find.byType(FlowyNetworkSvg)
              : find.byType(FlowyNetworkImage),
        );
        expect(image, findsOneWidget);
      } else {
        final image = find.descendant(
          of: find.byType(ViewTitleBar),
          matching: isSvg
              ? find.byWidgetPredicate((w) {
                  if (w is! SvgPicture) return false;
                  final loader = w.bytesLoader;
                  if (loader is! SvgFileLoader) return false;
                  return loader.file.path.endsWith('.svg');
                })
              : find.byType(Image),
        );
        expect(image, findsOneWidget);
      }
    }
  }

  void expectSelectedReminder(ReminderOption option) {
    final findSelectedText = find.descendant(
      of: find.byType(ReminderSelector),
      matching: find.text(option.label),
    );

    expect(findSelectedText, findsOneWidget);
  }

  void expectNotificationItems(int amount) {
    final findItems = find.byType(NotificationItem);

    expect(findItems, findsNWidgets(amount));
  }

  void expectToSeeRowDetailsPageDialog() {
    expect(
      find.descendant(
        of: find.byType(RowDetailPage),
        matching: find.byType(SimpleDialog),
      ),
      findsOneWidget,
    );
  }
}
