// 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 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:url_launcher/link.dart';
import 'package:url_launcher/src/link.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';

import 'mocks/mock_url_launcher_platform.dart';

void main() {
  late MockUrlLauncher mock;

  setUp(() {
    mock = MockUrlLauncher();
    UrlLauncherPlatform.instance = mock;
  });

  group('Link', () {
    testWidgets('handles null uri correctly', (WidgetTester tester) async {
      bool isBuilt = false;
      FollowLink? followLink;

      final Link link = Link(
        uri: null,
        builder: (BuildContext context, FollowLink? followLink2) {
          isBuilt = true;
          followLink = followLink2;
          return Container();
        },
      );
      await tester.pumpWidget(link);

      expect(link.isDisabled, isTrue);
      expect(isBuilt, isTrue);
      expect(followLink, isNull);
    });

    testWidgets('calls url_launcher for external URLs with blank target',
        (WidgetTester tester) async {
      FollowLink? followLink;

      await tester.pumpWidget(Link(
        uri: Uri.parse('http://example.com/foobar'),
        target: LinkTarget.blank,
        builder: (BuildContext context, FollowLink? followLink2) {
          followLink = followLink2;
          return Container();
        },
      ));

      mock
        ..setLaunchExpectations(
          url: 'http://example.com/foobar',
          launchMode: PreferredLaunchMode.externalApplication,
          universalLinksOnly: false,
          enableJavaScript: true,
          enableDomStorage: true,
          headers: <String, String>{},
          webOnlyWindowName: null,
          showTitle: false,
        )
        ..setResponse(true);
      await followLink!();
      // Calling canLaunch just to pre-check launch is an anti-pattern, since
      // canLaunch doesn't always work, so ensure that it's not called.
      expect(mock.canLaunchCalled, isFalse);
      expect(mock.launchCalled, isTrue);
    });

    testWidgets('calls url_launcher for external URLs with self target',
        (WidgetTester tester) async {
      FollowLink? followLink;

      await tester.pumpWidget(Link(
        uri: Uri.parse('http://example.com/foobar'),
        target: LinkTarget.self,
        builder: (BuildContext context, FollowLink? followLink2) {
          followLink = followLink2;
          return Container();
        },
      ));

      mock
        ..setLaunchExpectations(
          url: 'http://example.com/foobar',
          launchMode: PreferredLaunchMode.inAppBrowserView,
          universalLinksOnly: false,
          enableJavaScript: true,
          enableDomStorage: true,
          headers: <String, String>{},
          webOnlyWindowName: null,
          showTitle: false,
        )
        ..setResponse(true);
      await followLink!();
      // Calling canLaunch just to pre-check launch is an anti-pattern, since
      // canLaunch doesn't always work, so ensure that it's not called.
      expect(mock.canLaunchCalled, isFalse);
      expect(mock.launchCalled, isTrue);
    });

    testWidgets('pushes to framework for internal route names',
        (WidgetTester tester) async {
      final Uri uri = Uri.parse('/foo/bar');
      FollowLink? followLink;

      await tester.pumpWidget(MaterialApp(
        routes: <String, WidgetBuilder>{
          '/': (BuildContext context) => Link(
                uri: uri,
                builder: (BuildContext context, FollowLink? followLink2) {
                  followLink = followLink2;
                  return Container();
                },
              ),
          '/foo/bar': (BuildContext context) => Container(),
        },
      ));

      bool frameworkCalled = false;
      final Future<ByteData> Function(Object?, String) originalPushFunction =
          pushRouteToFrameworkFunction;
      pushRouteToFrameworkFunction = (Object? _, String __) {
        frameworkCalled = true;
        return Future<ByteData>.value(ByteData(0));
      };

      await followLink!();

      // Shouldn't use url_launcher when uri is an internal route name.
      expect(mock.canLaunchCalled, isFalse);
      expect(mock.launchCalled, isFalse);

      // A route should have been pushed to the framework.
      expect(frameworkCalled, true);

      // Restore the original function.
      pushRouteToFrameworkFunction = originalPushFunction;
    });
  });
}
