// 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 'dart:async';
import 'dart:convert';
import 'dart:io' show Directory, File, Process, ProcessSignal;

import '../framework/devices.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';

const String _messagePrefix = 'entrypoint:';
const String _entrypointName = 'entrypoint';

const String _dartCode =
    '''
import 'package:flutter/widgets.dart';

@pragma('vm:entry-point')
void main() {
  print('$_messagePrefix main');
  runApp(const ColoredBox(color: Color(0xffcc0000)));
}

@pragma('vm:entry-point')
void $_entrypointName() {
  print('$_messagePrefix $_entrypointName');
  runApp(const ColoredBox(color: Color(0xff00cc00)));
}
''';

const String _kotlinCode =
    '''
package com.example.entrypoint_dart_registrant

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
  override fun getDartEntrypointFunctionName(): String {
    return "$_entrypointName"
  }
}
''';

Future<TaskResult> _runWithTempDir(Directory tempDir) async {
  const String testDirName = 'entrypoint_dart_registrant';
  final String testPath = '${tempDir.path}/$testDirName';
  await inDirectory(tempDir, () async {
    await flutter('create', options: <String>['--platforms', 'android', testDirName]);
  });
  final String mainPath = '${tempDir.path}/$testDirName/lib/main.dart';
  print(mainPath);
  File(mainPath).writeAsStringSync(_dartCode);
  final String activityPath =
      '${tempDir.path}/$testDirName/android/app/src/main/kotlin/com/example/entrypoint_dart_registrant/MainActivity.kt';
  File(activityPath).writeAsStringSync(_kotlinCode);
  final Device device = await devices.workingDevice;
  await device.unlock();
  final String entrypoint = await inDirectory(testPath, () async {
    // The problem only manifested when the dart plugin registrant was used
    // (which path_provider has).
    await flutter('pub', options: <String>['add', 'path_provider:2.0.9']);
    // The problem only manifested on release builds, so we test release.
    final Process process = await startFlutter('run', options: <String>['--release']);
    final Completer<String> completer = Completer<String>();
    final StreamSubscription<String> stdoutSub = process.stdout
        .transform<String>(const Utf8Decoder())
        .transform<String>(const LineSplitter())
        .listen((String line) async {
          print(line);
          if (line.contains(_messagePrefix)) {
            completer.complete(line);
          }
        });
    final StreamSubscription<String> stderrSub = process.stderr
        .transform<String>(const Utf8Decoder())
        .transform<String>(const LineSplitter())
        .listen((String line) async {
          print(line);
        });
    final Object result = await Future.any(<Future<Object>>[completer.future, process.exitCode]);
    if (result is int) {
      throw Exception('flutter run failed, exitCode=$result');
    }
    final String entrypoint = result as String;
    await stdoutSub.cancel();
    await stderrSub.cancel();
    process.stdin.write('q');
    await process.stdin.flush();
    process.kill(ProcessSignal.sigint);
    return entrypoint;
  });
  if (entrypoint.contains('$_messagePrefix $_entrypointName')) {
    return TaskResult.success(null);
  } else {
    return TaskResult.failure('expected entrypoint:"$_entrypointName" but found:"$entrypoint"');
  }
}

/// Asserts that the custom entrypoint works in the presence of the dart plugin
/// registrant.
TaskFunction entrypointDartRegistrant() {
  return () async {
    final Directory tempDir = Directory.systemTemp.createTempSync('entrypoint_dart_registrant.');
    try {
      return await _runWithTempDir(tempDir);
    } finally {
      rmTree(tempDir);
    }
  };
}
