// 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 'dart:typed_data';

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';

import 'package:ui/src/engine.dart';

import '../common/fake_asset_manager.dart';
import '../common/test_initialization.dart';

void main() {
  internalBootstrapBrowserTest(() => testMain);
}

void testMain() {
  group('$SkiaFontCollection', () {
    setUpUnitTests();

    final List<String> warnings = <String>[];
    late void Function(String) oldPrintWarning;
    late FakeAssetScope testAssetScope;

    setUpAll(() async {
      oldPrintWarning = printWarning;
      printWarning = (String warning) {
        warnings.add(warning);
      };
    });

    tearDownAll(() {
      printWarning = oldPrintWarning;
    });

    setUp(() {
      testAssetScope = fakeAssetManager.pushAssetScope();
      mockHttpFetchResponseFactory = null;
      warnings.clear();
    });

    tearDown(() {
      fakeAssetManager.popAssetScope(testAssetScope);
      mockHttpFetchResponseFactory = null;
    });

    test('logs no warnings with the default mock asset manager', () async {
      final SkiaFontCollection fontCollection = SkiaFontCollection();
      await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager));

      expect(warnings, isEmpty);
    });

    test('logs a warning if one of the registered fonts is invalid', () async {
      mockHttpFetchResponseFactory = (String url) async {
        final ByteBuffer bogusData = Uint8List.fromList('this is not valid font data'.codeUnits).buffer;
        return MockHttpFetchResponse(
          status: 200,
          url: url,
          contentLength: bogusData.lengthInBytes,
          payload: MockHttpFetchPayload(byteBuffer: bogusData),
        );
      };
      final SkiaFontCollection fontCollection = SkiaFontCollection();
      testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data('''
[
   {
      "family":"Roboto",
      "fonts":[{"asset":"/fonts/Roboto-Regular.ttf"}]
   },
   {
      "family": "BrokenFont",
      "fonts":[{"asset":"packages/bogus/BrokenFont.ttf"}]
   }
  ]
      '''));
      // It should complete without error, but emit a warning about BrokenFont.
      await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager));
      expect(
        warnings,
        containsAllInOrder(
          <String>[
            'Failed to load font BrokenFont at packages/bogus/BrokenFont.ttf',
            'Verify that packages/bogus/BrokenFont.ttf contains a valid font.',
          ],
        ),
      );
    });

    test('logs an HTTP warning if one of the registered fonts is missing (404 file not found)', () async {
      final SkiaFontCollection fontCollection = SkiaFontCollection();
      testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data('''
[
   {
      "family":"Roboto",
      "fonts":[{"asset":"/fonts/Roboto-Regular.ttf"}]
   },
   {
      "family": "ThisFontDoesNotExist",
      "fonts":[{"asset":"packages/bogus/ThisFontDoesNotExist.ttf"}]
   }
  ]
      '''));

      // It should complete without error, but emit a warning about ThisFontDoesNotExist.
      await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager));
      expect(
        warnings,
        containsAllInOrder(<String>[
          'Font family ThisFontDoesNotExist not found (404) at packages/bogus/ThisFontDoesNotExist.ttf'
        ]),
      );
    });

    test('prioritizes Ahem loaded via FontManifest.json', () async {
      final SkiaFontCollection fontCollection = SkiaFontCollection();
      testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data('''
        [
          {
            "family":"Ahem",
            "fonts":[{"asset":"/assets/fonts/Roboto-Regular.ttf"}]
          }
        ]
      '''.trim()));

      final ByteBuffer robotoData = await httpFetchByteBuffer('/assets/fonts/Roboto-Regular.ttf');

      await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager));
      expect(warnings, isEmpty);

      // Use `singleWhere` to make sure only one version of 'Ahem' is loaded.
      final RegisteredFont ahem = fontCollection.debugRegisteredFonts!
        .singleWhere((RegisteredFont font) => font.family == 'Ahem');

      // Check that the contents of 'Ahem' is actually Roboto, because that's
      // what's specified in the manifest, and the manifest takes precedence.
      expect(ahem.bytes.length, robotoData.lengthInBytes);
    });

    test('falls back to default Ahem URL', () async {
      final SkiaFontCollection fontCollection = renderer.fontCollection as SkiaFontCollection;

      final ByteBuffer ahemData = await httpFetchByteBuffer('/assets/fonts/ahem.ttf');

      // Use `singleWhere` to make sure only one version of 'Ahem' is loaded.
      final RegisteredFont ahem = fontCollection.debugRegisteredFonts!
        .singleWhere((RegisteredFont font) => font.family == 'Ahem');

      // Check that the contents of 'Ahem' is actually Roboto, because that's
      // what's specified in the manifest, and the manifest takes precedence.
      expect(ahem.bytes.length, ahemData.lengthInBytes);
    });

    test('FlutterTest is the default test font', () async {
      final SkiaFontCollection fontCollection = renderer.fontCollection as SkiaFontCollection;

      expect(fontCollection.debugRegisteredFonts, isNotEmpty);
      expect(fontCollection.debugRegisteredFonts!.first.family, 'FlutterTest');
    });
  });
}
