// 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.

// @dart = 2.8

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter_devicelab/common.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;

TaskFunction dartPluginRegistryTest({
  String deviceIdOverride,
  Map<String, String> environment,
}) {
  final Directory tempDir = Directory.systemTemp
      .createTempSync('flutter_devicelab_dart_plugin_test.');
  return () async {
    try {
      section('Create implementation plugin');
      await inDirectory(tempDir, () async {
        await flutter(
          'create',
          options: <String>[
            '--template=plugin',
            '--org',
            'io.flutter.devicelab',
            '--platforms',
            'macos',
            'plugin_platform_implementation',
          ],
          environment: environment,
        );
      });

      final File pluginMain = File(path.join(
        tempDir.absolute.path,
        'plugin_platform_implementation',
        'lib',
        'plugin_platform_implementation.dart',
      ));
      if (!pluginMain.existsSync()) {
        return TaskResult.failure('${pluginMain.path} does not exist');
      }

      // Patch plugin main dart file.
      await pluginMain.writeAsString('''
class PluginPlatformInterfaceMacOS {
  static void registerWith() {
    print('PluginPlatformInterfaceMacOS.registerWith() was called');
  }
}
''', flush: true);

      // Patch plugin main pubspec file.
      final File pluginImplPubspec = File(path.join(
        tempDir.absolute.path,
        'plugin_platform_implementation',
        'pubspec.yaml',
      ));
      String pluginImplPubspecContent = await pluginImplPubspec.readAsString();
      pluginImplPubspecContent = pluginImplPubspecContent.replaceFirst(
        '        pluginClass: PluginPlatformImplementationPlugin',
        '        pluginClass: PluginPlatformImplementationPlugin\n'
            '        dartPluginClass: PluginPlatformInterfaceMacOS\n',
      );
      pluginImplPubspecContent = pluginImplPubspecContent.replaceFirst(
          '    platforms:\n',
          '    implements: plugin_platform_interface\n'
              '    platforms:\n');
      await pluginImplPubspec.writeAsString(pluginImplPubspecContent,
          flush: true);

      section('Create interface plugin');
      await inDirectory(tempDir, () async {
        await flutter(
          'create',
          options: <String>[
            '--template=plugin',
            '--org',
            'io.flutter.devicelab',
            '--platforms',
            'macos',
            'plugin_platform_interface',
          ],
          environment: environment,
        );
      });
      final File pluginInterfacePubspec = File(path.join(
        tempDir.absolute.path,
        'plugin_platform_interface',
        'pubspec.yaml',
      ));
      String pluginInterfacePubspecContent =
          await pluginInterfacePubspec.readAsString();
      pluginInterfacePubspecContent =
          pluginInterfacePubspecContent.replaceFirst(
              '        pluginClass: PluginPlatformInterfacePlugin',
              '        default_package: plugin_platform_implementation\n');
      pluginInterfacePubspecContent =
          pluginInterfacePubspecContent.replaceFirst(
              'dependencies:',
              'dependencies:\n'
                  '  plugin_platform_implementation:\n'
                  '    path: ../plugin_platform_implementation\n');
      await pluginInterfacePubspec.writeAsString(pluginInterfacePubspecContent,
          flush: true);

      section('Create app');

      await inDirectory(tempDir, () async {
        await flutter(
          'create',
          options: <String>[
            '--template=app',
            '--org',
            'io.flutter.devicelab',
            '--platforms',
            'macos',
            'app',
          ],
          environment: environment,
        );
      });

      final File appPubspec = File(path.join(
        tempDir.absolute.path,
        'app',
        'pubspec.yaml',
      ));
      String appPubspecContent = await appPubspec.readAsString();
      appPubspecContent = appPubspecContent.replaceFirst(
          'dependencies:',
          'dependencies:\n'
              '  plugin_platform_interface:\n'
              '    path: ../plugin_platform_interface\n');
      await appPubspec.writeAsString(appPubspecContent, flush: true);

      section('Flutter run for macos');

      await inDirectory(path.join(tempDir.path, 'app'), () async {
        final Process run = await startProcess(
          path.join(flutterDirectory.path, 'bin', 'flutter'),
          flutterCommandArgs('run', <String>['-d', 'macos', '-v']),
          environment: null,
        );
        Completer<void> registryExecutedCompleter = Completer<void>();
        final StreamSubscription<void> subscription = run.stdout
            .transform<String>(utf8.decoder)
            .transform<String>(const LineSplitter())
            .listen((String line) {
          if (line.contains(
              'PluginPlatformInterfaceMacOS.registerWith() was called')) {
            registryExecutedCompleter.complete();
          }
          print('stdout: $line');
        });

        section('Wait for registry execution');
        await registryExecutedCompleter.future;

        // Hot restart.
        run.stdin.write('R');
        registryExecutedCompleter = Completer<void>();

        section('Wait for registry execution after hot restart');
        await registryExecutedCompleter.future;

        unawaited(subscription.cancel());
        run.kill();
      });
      return TaskResult.success(null);
    } finally {
      rmTree(tempDir);
    }
  };
}
