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

void main() async {
  final projectRootPath = await Process.run(
    'git',
    ['rev-parse', '--show-toplevel'],
  ).then((result) => result.stdout.toString().trim());

  final backendPath = '$projectRootPath/backend';
  // Start the server
  print('🐸 Building Dart Frog server...');
  final build = await Process.start(
    'dart_frog',
    ['build'],
    workingDirectory: backendPath,
  );

  if (await build.exitCode != 0) {
    stdout.writeln('💀 Failed to build backend:');

    for (final element in [build.stdout, build.stderr]) {
      element
        ..transform(const SystemEncoding().decoder)
        ..listen((line) => stdout.write(line));
    }
    stdout.writeln();
    exit(1);
  }

  final serverLogs = <String>[];
  final server = await Process.start('dart', ['build/bin/server.dart'], workingDirectory: backendPath);
  server.stdout.transform(const SystemEncoding().decoder).listen(serverLogs.add);
  server.stderr.transform(const SystemEncoding().decoder).listen(serverLogs.add);
  unawaited(
    server.exitCode.then((code) async {
      if (code != 0) {
        stdout
          ..writeln('💀 Failed to run backend:\n')
          ..write(serverLogs.join('\n'));
        exit(1);
      }
    }),
  );

  // Wait for server to be ready
  print('⏲️ Waiting for server to be ready...');
  await _waitForServer();

  print('🤞 Running tests...');
  try {
    // Run the tests
    final testResult = await Process.run('dart', ['test', '$backendPath/test_e2e/tests/']);
    stdout.write(testResult.stdout);
    stderr.write(testResult.stderr);

    // Kill the server regardless of test result
    server.kill();

    if (testResult.exitCode != 0) {
      final sub = serverLogs.length > 10 ? serverLogs.length - 10 : 0;
      stdout.write("Server logs:\n${serverLogs.sublist(sub).join('\n')}");
    } else {
      stdout.writeln('💨 Passed like a light breeze 😮‍💨');
    }

    // Exit with the same code as the test process
    exit(testResult.exitCode);
  } catch (e) {
    print('💩 Error running tests 💩: $e');
    build.kill();
    server.kill();

    exit(1);
  }
}

Future<void> _waitForServer() async {
  final client = HttpClient();
  const maxAttempts = 30; // 30 seconds timeout
  var attempts = 0;

  while (attempts < maxAttempts) {
    try {
      final request =
          await client.getUrl(Uri.parse('http://localhost:8080/health')).timeout(const Duration(seconds: 1));
      final response = await request.close();
      await response.drain<void>();
      if (response.statusCode == 200) {
        print('Server is ready!');
        return;
      }
    } catch (e) {
      attempts++;
      await Future<void>.delayed(const Duration(seconds: 1));
    }
  }
  throw Exception('Server failed to start after $maxAttempts seconds');
}