import 'dart:io';

import 'package:flutter_test/flutter_test.dart';

void main() {
  test('Android launcher forwards args and pins the FVP SDK', () async {
    final project = await Directory.systemTemp.createTemp('smplayer-android-');
    addTearDown(() => project.delete(recursive: true));
    await File(
      '${project.path}/pubspec.yaml',
    ).writeAsString('version: 1.2.3\n');
    final staleFvpSdk = await _writeStaleFvpAndroidSdk(project);

    final bin = Directory('${project.path}/bin')..createSync();
    final log = File('${project.path}/flutter.log');
    await _writeFakeFlutter(bin, log);

    final environment = Map<String, String>.of(Platform.environment)
      ..['PATH'] =
          '${bin.path}${Platform.isWindows ? ';' : ':'}'
          '${Platform.environment['PATH'] ?? ''}'
      ..['ANDROID_HOME'] = ''
      ..['ANDROID_SDK_ROOT'] = ''
      ..['LOCALAPPDATA'] = project.path
      ..['SMPLAYER_TEST_LOG'] = log.path
      ..['FVP_DEPS_LATEST'] = 'stale';
    final result = await Process.run(_dartExecutable, [
      File('scripts/dev_android.dart').absolute.path,
      '--project-root',
      project.path,
      '--force',
      '--offline',
      '--',
      '--release',
      '--dart-define',
      'SMPLAYER_VERSION=9.9.9',
    ], environment: environment);

    expect(result.exitCode, 0, reason: '${result.stderr}');
    expect('${result.stdout}', contains('test-device'));
    expect('${result.stdout}', contains('App version: 1.2.3'));
    final calls = await log.readAsString();
    expect(calls, contains('ARGS:pub get --offline'));
    expect(
      calls,
      contains(
        'ARGS:run -d test-device --no-pub --release '
        '--dart-define SMPLAYER_VERSION=9.9.9',
      ),
    );
    expect(calls, contains('VERSION_CODE:1002003'));
    expect(calls, contains('FVP:$_fvpDepsUrl'));
    expect(calls, isNot(contains('LATEST:stale')));
    expect(staleFvpSdk.sdkDirectory.existsSync(), isFalse);
    expect(staleFvpSdk.sdkArchive.existsSync(), isFalse);
    expect(await staleFvpSdk.versionMarker.readAsString(), '0.37.0');
    if (!Platform.isWindows) {
      expect(calls, contains('STORAGE:https://storage.flutter-io.cn'));
      expect(calls, contains('PUB:https://pub.flutter-io.cn'));
    }
  });
}

Future<({File sdkArchive, Directory sdkDirectory, File versionMarker})>
_writeStaleFvpAndroidSdk(Directory project) async {
  final fvpDirectory = Directory('${project.path}/fvp')..createSync();
  final fvpRootUriWithoutTrailingSlash = fvpDirectory.uri
      .toString()
      .replaceFirst(RegExp(r'/$'), '');
  final fvpAndroidDirectory = Directory('${fvpDirectory.path}/android')
    ..createSync();
  final sdkDirectory = Directory(
    '${fvpAndroidDirectory.path}/mdk-sdk/lib/cmake',
  )..createSync(recursive: true);
  await File('${sdkDirectory.path}/FindMDK.cmake').writeAsString('stale');
  final sdkArchive = File('${fvpAndroidDirectory.path}/mdk-sdk-android.7z');
  await sdkArchive.writeAsString('stale');
  final versionMarker = File(
    '${fvpAndroidDirectory.path}/.smplayer-mdk-version',
  );
  await versionMarker.writeAsString('0.36.0');

  final packageConfigDirectory = Directory('${project.path}/.dart_tool')
    ..createSync();
  await File(
    '${packageConfigDirectory.path}/package_config.json',
  ).writeAsString('''
{
  "configVersion": 2,
  "packages": [
    {
      "name": "fvp",
      "rootUri": ${_jsonString(fvpRootUriWithoutTrailingSlash)},
      "packageUri": "lib/",
      "languageVersion": "3.0"
    }
  ]
}
''');

  return (
    sdkArchive: sdkArchive,
    sdkDirectory: Directory('${fvpAndroidDirectory.path}/mdk-sdk'),
    versionMarker: versionMarker,
  );
}

String _jsonString(String value) => '"${value.replaceAll('"', r'\"')}"';

Future<void> _writeFakeFlutter(Directory bin, File log) async {
  if (Platform.isWindows) {
    await File('${bin.path}/flutter.bat').writeAsString('''@echo off
if "%~1 %~2"=="devices --machine" (
  echo [{"id":"test-device","targetPlatform":"android-arm64"}]
  exit /b 0
)
>>"%SMPLAYER_TEST_LOG%" echo ARGS:%*
>>"%SMPLAYER_TEST_LOG%" echo FVP:%FVP_DEPS_URL%
>>"%SMPLAYER_TEST_LOG%" echo LATEST:%FVP_DEPS_LATEST%
>>"%SMPLAYER_TEST_LOG%" echo STORAGE:%FLUTTER_STORAGE_BASE_URL%
>>"%SMPLAYER_TEST_LOG%" echo PUB:%PUB_HOSTED_URL%
>>"%SMPLAYER_TEST_LOG%" echo VERSION_CODE:%SMPLAYER_ANDROID_VERSION_CODE%
>>"%SMPLAYER_TEST_LOG%" echo REQUIRE_SIGNING:%SMPLAYER_REQUIRE_RELEASE_SIGNING%
''');
    return;
  }

  final flutter = File('${bin.path}/flutter');
  await flutter.writeAsString(r'''#!/usr/bin/env bash
if [[ "$1 $2" == 'devices --machine' ]]; then
  printf '[{"id":"test-device","targetPlatform":"android-arm64"}]\n'
  exit 0
fi
printf 'ARGS:%s\n' "$*" >> "$SMPLAYER_TEST_LOG"
printf 'FVP:%s\n' "$FVP_DEPS_URL" >> "$SMPLAYER_TEST_LOG"
printf 'LATEST:%s\n' "${FVP_DEPS_LATEST-}" >> "$SMPLAYER_TEST_LOG"
printf 'STORAGE:%s\n' "$FLUTTER_STORAGE_BASE_URL" >> "$SMPLAYER_TEST_LOG"
printf 'PUB:%s\n' "$PUB_HOSTED_URL" >> "$SMPLAYER_TEST_LOG"
printf 'VERSION_CODE:%s\n' "$SMPLAYER_ANDROID_VERSION_CODE" >> "$SMPLAYER_TEST_LOG"
printf 'REQUIRE_SIGNING:%s\n' "$SMPLAYER_REQUIRE_RELEASE_SIGNING" >> "$SMPLAYER_TEST_LOG"
''');
  await Process.run('chmod', ['+x', flutter.path]);
}

const _fvpDepsUrl =
    'https://github.com/wang-bin/mdk-sdk/releases/download/v0.37.0';

String get _dartExecutable {
  final currentExecutable = File(Platform.resolvedExecutable);
  if (!currentExecutable.path.toLowerCase().contains('flutter_tester')) {
    return currentExecutable.path;
  }

  final flutterCacheDirectory = currentExecutable.parent.parent.parent.parent;
  final executableName = Platform.isWindows ? 'dart.exe' : 'dart';
  return File(
    '${flutterCacheDirectory.path}${Platform.pathSeparator}dart-sdk'
    '${Platform.pathSeparator}bin${Platform.pathSeparator}$executableName',
  ).path;
}
