// 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/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  test('ActionIconThemeData copyWith, ==, hashCode basics', () {
    expect(const ActionIconThemeData(), const ActionIconThemeData().copyWith());
    expect(const ActionIconThemeData().hashCode,
        const ActionIconThemeData().copyWith().hashCode);
  });

  testWidgets('ActionIconThemeData copyWith overrides all properties', (WidgetTester tester) async {
    // This is a regression test for https://github.com/flutter/flutter/issues/126762.
    Widget originalButtonBuilder(BuildContext context) {
      return const SizedBox();
    }
    Widget newButtonBuilder(BuildContext context) {
      return const Icon(Icons.add);
    }

    // Create a ActionIconThemeData with all properties set.
    final ActionIconThemeData original = ActionIconThemeData(
      backButtonIconBuilder: originalButtonBuilder,
      closeButtonIconBuilder: originalButtonBuilder,
      drawerButtonIconBuilder: originalButtonBuilder,
      endDrawerButtonIconBuilder: originalButtonBuilder,
    );
    // Check if the all properties are copied.
    final ActionIconThemeData copy = original.copyWith();
    expect(copy.backButtonIconBuilder, originalButtonBuilder);
    expect(copy.closeButtonIconBuilder, originalButtonBuilder);
    expect(copy.drawerButtonIconBuilder, originalButtonBuilder);
    expect(copy.endDrawerButtonIconBuilder, originalButtonBuilder);

    // Check if the properties are overridden.
    final ActionIconThemeData overridden = original.copyWith(
      backButtonIconBuilder: newButtonBuilder,
      closeButtonIconBuilder: newButtonBuilder,
      drawerButtonIconBuilder: newButtonBuilder,
      endDrawerButtonIconBuilder: newButtonBuilder,
    );
    expect(overridden.backButtonIconBuilder, newButtonBuilder);
    expect(overridden.closeButtonIconBuilder, newButtonBuilder);
    expect(overridden.drawerButtonIconBuilder, newButtonBuilder);
    expect(overridden.endDrawerButtonIconBuilder, newButtonBuilder);
  });

  test('ActionIconThemeData defaults', () {
    const ActionIconThemeData themeData = ActionIconThemeData();
    expect(themeData.backButtonIconBuilder, null);
    expect(themeData.closeButtonIconBuilder, null);
    expect(themeData.drawerButtonIconBuilder, null);
    expect(themeData.endDrawerButtonIconBuilder, null);
  });

  testWidgets('Default ActionIconThemeData debugFillProperties',
      (WidgetTester tester) async {
    final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
    const ActionIconThemeData().debugFillProperties(builder);

    final List<String> description = builder.properties
        .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
        .map((DiagnosticsNode node) => node.toString())
        .toList();

    expect(description, <String>[]);
  });

  testWidgets('ActionIconThemeData implements debugFillProperties',
      (WidgetTester tester) async {
    Widget actionButtonIconBuilder(BuildContext context) {
      return const Icon(IconData(0));
    }

    final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
    ActionIconThemeData(
      backButtonIconBuilder: actionButtonIconBuilder,
      closeButtonIconBuilder: actionButtonIconBuilder,
      drawerButtonIconBuilder: actionButtonIconBuilder,
      endDrawerButtonIconBuilder: actionButtonIconBuilder,
    ).debugFillProperties(builder);

    final List<String> description = builder.properties
        .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
        .map((DiagnosticsNode node) => node.toString())
        .toList();

    final Matcher containsBuilderCallback = contains('Closure: (BuildContext) =>');
    expect(description, <dynamic>[
      allOf(startsWith('backButtonIconBuilder:'), containsBuilderCallback),
      allOf(startsWith('closeButtonIconBuilder:'), containsBuilderCallback),
      allOf(startsWith('drawerButtonIconBuilder:'), containsBuilderCallback),
      allOf(startsWith('endDrawerButtonIconBuilder:'), containsBuilderCallback),
    ]);
  });

  testWidgets('Action buttons use ThemeData action icon theme', (WidgetTester tester) async {
    const Color green = Color(0xff00ff00);
    const IconData icon = IconData(0);

    Widget buildSampleIcon(BuildContext context) {
      return const Icon(
        icon,
        size: 20,
        color: green,
      );
    }

    final ActionIconThemeData actionIconTheme = ActionIconThemeData(
      backButtonIconBuilder: buildSampleIcon,
      closeButtonIconBuilder: buildSampleIcon,
      drawerButtonIconBuilder: buildSampleIcon,
      endDrawerButtonIconBuilder: buildSampleIcon,
    );

    await tester.pumpWidget(
      MaterialApp(
        theme: ThemeData.light(useMaterial3: true).copyWith(
          actionIconTheme: actionIconTheme,
        ),
        home: const Material(
          child: Column(
            children: <Widget>[
              BackButton(),
              CloseButton(),
              DrawerButton(),
              EndDrawerButton(),
            ],
          ),
        ),
      ),
    );

    final Icon backButtonIcon = tester.widget(find.descendant(of: find.byType(BackButton), matching: find.byType(Icon)));
    final Icon closeButtonIcon = tester.widget(find.descendant(of: find.byType(CloseButton), matching: find.byType(Icon)));
    final Icon drawerButtonIcon = tester.widget(find.descendant(of: find.byType(DrawerButton), matching: find.byType(Icon)));
    final Icon endDrawerButtonIcon = tester.widget(find.descendant(of: find.byType(EndDrawerButton), matching: find.byType(Icon)));

    expect(backButtonIcon.icon == icon, isTrue);
    expect(closeButtonIcon.icon == icon, isTrue);
    expect(drawerButtonIcon.icon == icon, isTrue);
    expect(endDrawerButtonIcon.icon == icon, isTrue);

    final RichText backButtonIconText = tester.widget(find.descendant(of: find.byType(BackButton), matching: find.byType(RichText)));
    final RichText closeButtonIconText = tester.widget(find.descendant(of: find.byType(CloseButton), matching: find.byType(RichText)));
    final RichText drawerButtonIconText = tester.widget(find.descendant(of: find.byType(DrawerButton), matching: find.byType(RichText)));
    final RichText endDrawerButtonIconText = tester.widget(find.descendant(of: find.byType(EndDrawerButton), matching: find.byType(RichText)));

    expect(backButtonIconText.text.style!.color, green);
    expect(closeButtonIconText.text.style!.color, green);
    expect(drawerButtonIconText.text.style!.color, green);
    expect(endDrawerButtonIconText.text.style!.color, green);
  });

  // This test is essentially the same as 'Action buttons use ThemeData action icon theme'. In
  // this case the theme is introduced with the ActionIconTheme widget instead of
  // ThemeData.actionIconTheme.
  testWidgets('Action buttons use ActionIconTheme', (WidgetTester tester) async {
    const Color green = Color(0xff00ff00);
    const IconData icon = IconData(0);

    Widget buildSampleIcon(BuildContext context) {
      return const Icon(
        icon,
        size: 20,
        color: green,
      );
    }

    final ActionIconThemeData actionIconTheme = ActionIconThemeData(
      backButtonIconBuilder: buildSampleIcon,
      closeButtonIconBuilder: buildSampleIcon,
      drawerButtonIconBuilder: buildSampleIcon,
      endDrawerButtonIconBuilder: buildSampleIcon,
    );

    await tester.pumpWidget(
      MaterialApp(
        home: ActionIconTheme(
          data: actionIconTheme,
          child: const Material(
            child: Column(
              children: <Widget>[
                BackButton(),
                CloseButton(),
                DrawerButton(),
                EndDrawerButton(),
              ],
            ),
          ),
        ),
      ),
    );

    final Icon backButtonIcon = tester.widget(find.descendant(of: find.byType(BackButton), matching: find.byType(Icon)));
    final Icon closeButtonIcon = tester.widget(find.descendant(of: find.byType(CloseButton), matching: find.byType(Icon)));
    final Icon drawerButtonIcon = tester.widget(find.descendant(of: find.byType(DrawerButton), matching: find.byType(Icon)));
    final Icon endDrawerButtonIcon = tester.widget(find.descendant(of: find.byType(EndDrawerButton), matching: find.byType(Icon)));

    expect(backButtonIcon.icon == icon, isTrue);
    expect(closeButtonIcon.icon == icon, isTrue);
    expect(drawerButtonIcon.icon == icon, isTrue);
    expect(endDrawerButtonIcon.icon == icon, isTrue);

    final RichText backButtonIconText = tester.widget(find.descendant(of: find.byType(BackButton), matching: find.byType(RichText)));
    final RichText closeButtonIconText = tester.widget(find.descendant(of: find.byType(CloseButton), matching: find.byType(RichText)));
    final RichText drawerButtonIconText = tester.widget(find.descendant(of: find.byType(DrawerButton), matching: find.byType(RichText)));
    final RichText endDrawerButtonIconText = tester.widget(find.descendant(of: find.byType(EndDrawerButton), matching: find.byType(RichText)));

    expect(backButtonIconText.text.style!.color, green);
    expect(closeButtonIconText.text.style!.color, green);
    expect(drawerButtonIconText.text.style!.color, green);
    expect(endDrawerButtonIconText.text.style!.color, green);
  });
}