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

void main() {
  test('Typography is defined for all target platforms', () {
    for (final TargetPlatform platform in TargetPlatform.values) {
      final Typography typography = Typography.material2018(platform: platform);
      expect(typography, isNotNull, reason: 'null typography for $platform');
      expect(typography.black, isNotNull, reason: 'null black typography for $platform');
      expect(typography.white, isNotNull, reason: 'null white typography for $platform');
    }
  });

  test('Typography lerp special cases', () {
    final Typography typography = Typography();
    expect(identical(Typography.lerp(typography, typography, 0.5), typography), true);
  });

  test('Typography on non-Apple platforms defaults to the correct font', () {
    expect(Typography.material2018().black.titleLarge!.fontFamily, 'Roboto');
    expect(Typography.material2018(platform: TargetPlatform.fuchsia).black.titleLarge!.fontFamily, 'Roboto');
    expect(Typography.material2018(platform: TargetPlatform.linux).black.titleLarge!.fontFamily, 'Roboto');
    expect(Typography.material2018(platform: TargetPlatform.linux).black.titleLarge!.fontFamilyFallback, <String>['Ubuntu', 'Cantarell', 'DejaVu Sans', 'Liberation Sans', 'Arial']);
    expect(Typography.material2018(platform: TargetPlatform.windows).black.titleLarge!.fontFamily, 'Segoe UI');
    expect(Typography.material2018().white.titleLarge!.fontFamily, 'Roboto');
    expect(Typography.material2018(platform: TargetPlatform.fuchsia).white.titleLarge!.fontFamily, 'Roboto');
    expect(Typography.material2018(platform: TargetPlatform.linux).white.titleLarge!.fontFamily, 'Roboto');
    expect(Typography.material2018(platform: TargetPlatform.linux).white.titleLarge!.fontFamilyFallback, <String>['Ubuntu', 'Cantarell', 'DejaVu Sans', 'Liberation Sans', 'Arial']);
    expect(Typography.material2018(platform: TargetPlatform.windows).white.titleLarge!.fontFamily, 'Segoe UI');
  });

  // Ref: https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/
  final Matcher isSanFranciscoDisplayFont = predicate((TextStyle s) {
    return s.fontFamily == 'CupertinoSystemDisplay';
  }, 'Uses SF Display font');

  final Matcher isSanFranciscoTextFont = predicate((TextStyle s) {
    return s.fontFamily == 'CupertinoSystemText';
  }, 'Uses SF Text font');

  final Matcher isMacOSSanFranciscoMetaFont = predicate((TextStyle s) {
    return s.fontFamily == '.AppleSystemUIFont';
  }, 'Uses macOS system meta-font');

  test('Typography on iOS defaults to the correct SF font family based on size', () {
    final Typography typography = Typography.material2018(platform: TargetPlatform.iOS);
    for (final TextTheme textTheme in <TextTheme>[typography.black, typography.white]) {
      expect(textTheme.displayLarge, isSanFranciscoDisplayFont);
      expect(textTheme.displayMedium, isSanFranciscoDisplayFont);
      expect(textTheme.displaySmall, isSanFranciscoDisplayFont);
      expect(textTheme.headlineLarge, isSanFranciscoDisplayFont);
      expect(textTheme.headlineMedium, isSanFranciscoDisplayFont);
      expect(textTheme.headlineSmall, isSanFranciscoDisplayFont);
      expect(textTheme.titleLarge, isSanFranciscoDisplayFont);
      expect(textTheme.titleMedium, isSanFranciscoTextFont);
      expect(textTheme.titleSmall, isSanFranciscoTextFont);
      expect(textTheme.bodyLarge, isSanFranciscoTextFont);
      expect(textTheme.bodyMedium, isSanFranciscoTextFont);
      expect(textTheme.bodySmall, isSanFranciscoTextFont);
      expect(textTheme.labelLarge, isSanFranciscoTextFont);
      expect(textTheme.labelMedium, isSanFranciscoTextFont);
      expect(textTheme.labelSmall, isSanFranciscoTextFont);
    }
  });

  test('Typography on macOS defaults to the system UI meta-font', () {
    final Typography typography = Typography.material2018(platform: TargetPlatform.macOS);
    for (final TextTheme textTheme in <TextTheme>[typography.black, typography.white]) {
      expect(textTheme.displayLarge, isMacOSSanFranciscoMetaFont);
      expect(textTheme.displayMedium, isMacOSSanFranciscoMetaFont);
      expect(textTheme.displaySmall, isMacOSSanFranciscoMetaFont);
      expect(textTheme.headlineLarge, isMacOSSanFranciscoMetaFont);
      expect(textTheme.headlineMedium, isMacOSSanFranciscoMetaFont);
      expect(textTheme.headlineSmall, isMacOSSanFranciscoMetaFont);
      expect(textTheme.titleLarge, isMacOSSanFranciscoMetaFont);
      expect(textTheme.titleMedium, isMacOSSanFranciscoMetaFont);
      expect(textTheme.titleSmall, isMacOSSanFranciscoMetaFont);
      expect(textTheme.bodyLarge, isMacOSSanFranciscoMetaFont);
      expect(textTheme.bodyMedium, isMacOSSanFranciscoMetaFont);
      expect(textTheme.bodySmall, isMacOSSanFranciscoMetaFont);
      expect(textTheme.labelLarge, isMacOSSanFranciscoMetaFont);
      expect(textTheme.labelMedium, isMacOSSanFranciscoMetaFont);
      expect(textTheme.labelSmall, isMacOSSanFranciscoMetaFont);
    }
  });

  testWidgets('Typography implements debugFillProperties', (WidgetTester tester) async {
    final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
    Typography.material2014(
      black: Typography.blackCupertino,
      white: Typography.whiteCupertino,
      englishLike: Typography.englishLike2018,
      dense: Typography.dense2018,
      tall: Typography.tall2018,
    ).debugFillProperties(builder);

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

    expect(nonDefaultPropertyNames, <String>['black', 'white', 'englishLike', 'dense', 'tall']);
  });

  test('Can lerp between different typographies', () {
    final List<Typography> all = <Typography>[
      for (final TargetPlatform platform in TargetPlatform.values) Typography.material2014(platform: platform),
      for (final TargetPlatform platform in TargetPlatform.values) Typography.material2018(platform: platform),
      for (final TargetPlatform platform in TargetPlatform.values) Typography.material2021(platform: platform),
    ];

    for (final Typography fromTypography in all) {
      for (final Typography toTypography in all) {
        Object? error;
        try {
          Typography.lerp(fromTypography, toTypography, 0.5);
        } catch (e) {
          error = e;
        }
        expect(error, isNull);
      }
    }
  });

  test('englishLike2018 TextTheme matches Material Design spec', () {
    // Check the default material text theme against the style values
    // shown https://material.io/design/typography/#type-scale.

    final TextTheme theme = Typography.englishLike2018.merge(Typography.blackMountainView);
    const FontWeight light = FontWeight.w300;
    const FontWeight regular = FontWeight.w400;
    const FontWeight medium = FontWeight.w500;

    // H1 Roboto light 96 -1.5
    expect(theme.displayLarge!.fontFamily, 'Roboto');
    expect(theme.displayLarge!.fontWeight, light);
    expect(theme.displayLarge!.fontSize, 96);
    expect(theme.displayLarge!.letterSpacing, -1.5);

    // H2 Roboto light 60 -0.5
    expect(theme.displayMedium!.fontFamily, 'Roboto');
    expect(theme.displayMedium!.fontWeight, light);
    expect(theme.displayMedium!.fontSize, 60);
    expect(theme.displayMedium!.letterSpacing, -0.5);

    // H3 Roboto regular 48 0
    expect(theme.displaySmall!.fontFamily, 'Roboto');
    expect(theme.displaySmall!.fontWeight, regular);
    expect(theme.displaySmall!.fontSize, 48);
    expect(theme.displaySmall!.letterSpacing, 0);

    // Headline Large (from Material 3 for backwards compatibility) Roboto regular 40 0.25
    expect(theme.headlineLarge!.fontFamily, 'Roboto');
    expect(theme.headlineLarge!.fontWeight, regular);
    expect(theme.headlineLarge!.fontSize, 40);
    expect(theme.headlineLarge!.letterSpacing, 0.25);

    // H4 Roboto regular 34 0.25
    expect(theme.headlineMedium!.fontFamily, 'Roboto');
    expect(theme.headlineMedium!.fontWeight, regular);
    expect(theme.headlineMedium!.fontSize, 34);
    expect(theme.headlineMedium!.letterSpacing, 0.25);

    // H5 Roboto regular 24 0
    expect(theme.headlineSmall!.fontFamily, 'Roboto');
    expect(theme.headlineSmall!.fontWeight, regular);
    expect(theme.headlineSmall!.fontSize, 24);
    expect(theme.headlineSmall!.letterSpacing, 0);

    // H6 Roboto medium 20 0.15
    expect(theme.titleLarge!.fontFamily, 'Roboto');
    expect(theme.titleLarge!.fontWeight, medium);
    expect(theme.titleLarge!.fontSize, 20);
    expect(theme.titleLarge!.letterSpacing, 0.15);

    // Subtitle1 Roboto regular 16 0.15
    expect(theme.titleMedium!.fontFamily, 'Roboto');
    expect(theme.titleMedium!.fontWeight, regular);
    expect(theme.titleMedium!.fontSize, 16);
    expect(theme.titleMedium!.letterSpacing, 0.15);

    // Subtitle2 Roboto medium 14 0.1
    expect(theme.titleSmall!.fontFamily, 'Roboto');
    expect(theme.titleSmall!.fontWeight, medium);
    expect(theme.titleSmall!.fontSize, 14);
    expect(theme.titleSmall!.letterSpacing, 0.1);

    // Body1 Roboto regular 16 0.5
    expect(theme.bodyLarge!.fontFamily, 'Roboto');
    expect(theme.bodyLarge!.fontWeight, regular);
    expect(theme.bodyLarge!.fontSize, 16);
    expect(theme.bodyLarge!.letterSpacing, 0.5);

    // Body2 Roboto regular 14 0.25
    expect(theme.bodyMedium!.fontFamily, 'Roboto');
    expect(theme.bodyMedium!.fontWeight, regular);
    expect(theme.bodyMedium!.fontSize, 14);
    expect(theme.bodyMedium!.letterSpacing, 0.25);

    // Caption Roboto regular 12 0.4
    expect(theme.bodySmall!.fontFamily, 'Roboto');
    expect(theme.bodySmall!.fontWeight, regular);
    expect(theme.bodySmall!.fontSize, 12);
    expect(theme.bodySmall!.letterSpacing, 0.4);

    // BUTTON Roboto medium 14 1.25
    expect(theme.labelLarge!.fontFamily, 'Roboto');
    expect(theme.labelLarge!.fontWeight, medium);
    expect(theme.labelLarge!.fontSize, 14);
    expect(theme.labelLarge!.letterSpacing, 1.25);

    // Label Medium (from Material 3 for backwards compatibility) Roboto regular 11 1.5
    expect(theme.labelMedium!.fontFamily, 'Roboto');
    expect(theme.labelMedium!.fontWeight, regular);
    expect(theme.labelMedium!.fontSize, 11);
    expect(theme.labelMedium!.letterSpacing, 1.5);

    // OVERLINE Roboto regular 10 1.5
    expect(theme.labelSmall!.fontFamily, 'Roboto');
    expect(theme.labelSmall!.fontWeight, regular);
    expect(theme.labelSmall!.fontSize, 10);
    expect(theme.labelSmall!.letterSpacing, 1.5);
  });

  test('englishLike2021 TextTheme matches Material Design 3 spec', () {
    // Check the default material text theme against the style values
    // shown https://m3.material.io/styles/typography/tokens.
    //
    // This may need to be updated if the token values change.
    final TextTheme theme = Typography.englishLike2021.merge(Typography.blackMountainView);

    // Display large
    expect(theme.displayLarge!.fontFamily, 'Roboto');
    expect(theme.displayLarge!.fontSize, 57.0);
    expect(theme.displayLarge!.fontWeight, FontWeight.w400);
    expect(theme.displayLarge!.letterSpacing, -0.25);
    expect(theme.displayLarge!.height, 1.12);
    expect(theme.displayLarge!.textBaseline, TextBaseline.alphabetic);
    expect(theme.displayLarge!.leadingDistribution, TextLeadingDistribution.even);

    // Display medium
    expect(theme.displayMedium!.fontFamily, 'Roboto');
    expect(theme.displayMedium!.fontSize, 45.0);
    expect(theme.displayMedium!.fontWeight, FontWeight.w400);
    expect(theme.displayMedium!.letterSpacing, 0.0);
    expect(theme.displayMedium!.height, 1.16);
    expect(theme.displayMedium!.textBaseline, TextBaseline.alphabetic);
    expect(theme.displayMedium!.leadingDistribution, TextLeadingDistribution.even);

    // Display small
    expect(theme.displaySmall!.fontFamily, 'Roboto');
    expect(theme.displaySmall!.fontSize, 36.0);
    expect(theme.displaySmall!.fontWeight, FontWeight.w400);
    expect(theme.displaySmall!.letterSpacing, 0.0);
    expect(theme.displaySmall!.height, 1.22);
    expect(theme.displaySmall!.textBaseline, TextBaseline.alphabetic);
    expect(theme.displaySmall!.leadingDistribution, TextLeadingDistribution.even);

    // Headline large
    expect(theme.headlineLarge!.fontFamily, 'Roboto');
    expect(theme.headlineLarge!.fontSize, 32.0);
    expect(theme.headlineLarge!.fontWeight, FontWeight.w400);
    expect(theme.headlineLarge!.letterSpacing, 0.0);
    expect(theme.headlineLarge!.height, 1.25);
    expect(theme.headlineLarge!.textBaseline, TextBaseline.alphabetic);
    expect(theme.headlineLarge!.leadingDistribution, TextLeadingDistribution.even);

    // Headline medium
    expect(theme.headlineMedium!.fontFamily, 'Roboto');
    expect(theme.headlineMedium!.fontSize, 28.0);
    expect(theme.headlineMedium!.fontWeight, FontWeight.w400);
    expect(theme.headlineMedium!.letterSpacing, 0.0);
    expect(theme.headlineMedium!.height, 1.29);
    expect(theme.headlineMedium!.textBaseline, TextBaseline.alphabetic);
    expect(theme.headlineMedium!.leadingDistribution, TextLeadingDistribution.even);

    // Headline small
    expect(theme.headlineSmall!.fontFamily, 'Roboto');
    expect(theme.headlineSmall!.fontSize, 24.0);
    expect(theme.headlineSmall!.fontWeight, FontWeight.w400);
    expect(theme.headlineSmall!.letterSpacing, 0.0);
    expect(theme.headlineSmall!.height, 1.33);
    expect(theme.headlineSmall!.textBaseline, TextBaseline.alphabetic);
    expect(theme.headlineSmall!.leadingDistribution, TextLeadingDistribution.even);

    // Title large
    expect(theme.titleLarge!.fontFamily, 'Roboto');
    expect(theme.titleLarge!.fontSize, 22.0);
    expect(theme.titleLarge!.fontWeight, FontWeight.w400);
    expect(theme.titleLarge!.letterSpacing, 0.0);
    expect(theme.titleLarge!.height, 1.27);
    expect(theme.titleLarge!.textBaseline, TextBaseline.alphabetic);
    expect(theme.titleLarge!.leadingDistribution, TextLeadingDistribution.even);

    // Title medium
    expect(theme.titleMedium!.fontFamily, 'Roboto');
    expect(theme.titleMedium!.fontSize, 16.0);
    expect(theme.titleMedium!.fontWeight, FontWeight.w500);
    expect(theme.titleMedium!.letterSpacing, 0.15);
    expect(theme.titleMedium!.height, 1.50);
    expect(theme.titleMedium!.textBaseline, TextBaseline.alphabetic);
    expect(theme.titleMedium!.leadingDistribution, TextLeadingDistribution.even);

    // Title small
    expect(theme.titleSmall!.fontFamily, 'Roboto');
    expect(theme.titleSmall!.fontSize, 14.0);
    expect(theme.titleSmall!.fontWeight, FontWeight.w500);
    expect(theme.titleSmall!.letterSpacing, 0.1);
    expect(theme.titleSmall!.height, 1.43);
    expect(theme.titleSmall!.textBaseline, TextBaseline.alphabetic);
    expect(theme.titleSmall!.leadingDistribution, TextLeadingDistribution.even);

    // Label large
    expect(theme.labelLarge!.fontFamily, 'Roboto');
    expect(theme.labelLarge!.fontSize, 14.0);
    expect(theme.labelLarge!.fontWeight, FontWeight.w500);
    expect(theme.labelLarge!.letterSpacing, 0.1);
    expect(theme.labelLarge!.height, 1.43);
    expect(theme.labelLarge!.textBaseline, TextBaseline.alphabetic);
    expect(theme.labelLarge!.leadingDistribution, TextLeadingDistribution.even);

    // Label medium
    expect(theme.labelMedium!.fontFamily, 'Roboto');
    expect(theme.labelMedium!.fontSize, 12.0);
    expect(theme.labelMedium!.fontWeight, FontWeight.w500);
    expect(theme.labelMedium!.letterSpacing, 0.5);
    expect(theme.labelMedium!.height, 1.33);
    expect(theme.labelMedium!.textBaseline, TextBaseline.alphabetic);
    expect(theme.labelMedium!.leadingDistribution, TextLeadingDistribution.even);

    // Label small
    expect(theme.labelSmall!.fontFamily, 'Roboto');
    expect(theme.labelSmall!.fontSize, 11.0);
    expect(theme.labelSmall!.fontWeight, FontWeight.w500);
    expect(theme.labelSmall!.letterSpacing, 0.5);
    expect(theme.labelSmall!.height, 1.45);
    expect(theme.labelSmall!.textBaseline, TextBaseline.alphabetic);
    expect(theme.labelSmall!.leadingDistribution, TextLeadingDistribution.even);

    // Body large
    expect(theme.bodyLarge!.fontFamily, 'Roboto');
    expect(theme.bodyLarge!.fontSize, 16.0);
    expect(theme.bodyLarge!.fontWeight, FontWeight.w400);
    expect(theme.bodyLarge!.letterSpacing, 0.5);
    expect(theme.bodyLarge!.height, 1.50);
    expect(theme.bodyLarge!.textBaseline, TextBaseline.alphabetic);
    expect(theme.bodyLarge!.leadingDistribution, TextLeadingDistribution.even);

    // Body medium
    expect(theme.bodyMedium!.fontFamily, 'Roboto');
    expect(theme.bodyMedium!.fontSize, 14.0);
    expect(theme.bodyMedium!.fontWeight, FontWeight.w400);
    expect(theme.bodyMedium!.letterSpacing, 0.25);
    expect(theme.bodyMedium!.height, 1.43);
    expect(theme.bodyMedium!.textBaseline, TextBaseline.alphabetic);
    expect(theme.bodyMedium!.leadingDistribution, TextLeadingDistribution.even);

    // Body small
    expect(theme.bodySmall!.fontFamily, 'Roboto');
    expect(theme.bodySmall!.fontSize, 12.0);
    expect(theme.bodySmall!.fontWeight, FontWeight.w400);
    expect(theme.bodySmall!.letterSpacing, 0.4);
    expect(theme.bodySmall!.height, 1.33);
    expect(theme.bodySmall!.textBaseline, TextBaseline.alphabetic);
    expect(theme.bodySmall!.leadingDistribution, TextLeadingDistribution.even);
  });

  test('Default M3 light textTheme styles all use onSurface', () {
    final ThemeData theme = ThemeData(useMaterial3: true);
    final TextTheme textTheme = theme.textTheme;
    final Color dark = theme.colorScheme.onSurface;
    expect(textTheme.displayLarge!.color, dark);
    expect(textTheme.displayMedium!.color, dark);
    expect(textTheme.displaySmall!.color, dark);
    expect(textTheme.headlineLarge!.color, dark);
    expect(textTheme.headlineMedium!.color, dark);
    expect(textTheme.headlineSmall!.color, dark);
    expect(textTheme.titleLarge!.color, dark);
    expect(textTheme.titleMedium!.color, dark);
    expect(textTheme.titleSmall!.color, dark);
    expect(textTheme.bodyLarge!.color, dark);
    expect(textTheme.bodyMedium!.color, dark);
    expect(textTheme.bodySmall!.color, dark);
    expect(textTheme.labelLarge!.color, dark);
    expect(textTheme.labelMedium!.color, dark);
    expect(textTheme.labelSmall!.color, dark);
  });

  test('Default M3 dark textTheme styles all use onSurface', () {
    final ThemeData theme = ThemeData(useMaterial3: true, brightness: Brightness.dark);
    final TextTheme textTheme = theme.textTheme;
    final Color light = theme.colorScheme.onSurface;
    expect(textTheme.displayLarge!.color, light);
    expect(textTheme.displayMedium!.color, light);
    expect(textTheme.displaySmall!.color, light);
    expect(textTheme.headlineLarge!.color, light);
    expect(textTheme.headlineMedium!.color, light);
    expect(textTheme.headlineSmall!.color, light);
    expect(textTheme.titleLarge!.color, light);
    expect(textTheme.titleMedium!.color, light);
    expect(textTheme.titleSmall!.color, light);
    expect(textTheme.bodyLarge!.color, light);
    expect(textTheme.bodyMedium!.color, light);
    expect(textTheme.bodySmall!.color, light);
    expect(textTheme.labelLarge!.color, light);
    expect(textTheme.labelMedium!.color, light);
    expect(textTheme.labelSmall!.color, light);
  });
}