// 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:io';

void _validate(List<String> args) {
  bool errors = false;
  if (!File('bin/internal/engine.version').existsSync()) {
    errors = true;
    print('This program must be run from the root of your flutter repository.');
  }
  if (!File('../engine/src/flutter/DEPS').existsSync()) {
    errors = true;
    print('This program assumes the engine directory is a sibling to the flutter repository directory.');
  }
  if (args.length != 1) {
    errors = true;
    print('This program takes the engine revision as a single argument.');
  }
  if (errors) {
    exit(-1);
  }
}

const String engineRepo = '../engine/src/flutter';

Future<void> main(List<String> args) async {
  _validate(args);
  await _fetchUpstream();
  await _fetchUpstream(engineRepo);
  String? flutterRevision;
  await for (final FlutterEngineRevision revision in _logEngineVersions()) {
    if (!await containsRevision(args[0], revision.engineRevision)) {
      if (flutterRevision == null) {
        print('Revision not found.');
        exit(-1);
      }
      print('earliest revision: $flutterRevision');
      print('Tags that contain this engine revision:');
      print(await _tagsForRevision(flutterRevision));
      exit(0);
    }
    flutterRevision = revision.flutterRevision;
  }
}

Future<void> _fetchUpstream([String workingDirectory = '.']) async {
  print('Fetching remotes for "$workingDirectory" - you may be prompted for SSH credentials by git.');
  final ProcessResult fetchResult = await Process.run(
    'git',
    <String>[
      'fetch',
      '--all',
    ],
    workingDirectory: workingDirectory,
  );
  if (fetchResult.exitCode != 0) {
    throw Exception('Failed to fetch upstream in repository $workingDirectory');
  }
}

Future<String> _tagsForRevision(String flutterRevision) async {
  final ProcessResult tagResult = await Process.run(
    'git',
    <String>[
      'tag',
      '--contains',
      flutterRevision,
    ],
  );
  return tagResult.stdout as String;
}

Future<bool> containsRevision(String ancestorRevision, String revision) async {
  final ProcessResult result = await Process.run(
    'git',
    <String>[
      'merge-base',
      '--is-ancestor',
      ancestorRevision,
      revision,
    ],
    workingDirectory: engineRepo,
  );
  return result.exitCode == 0;
}

Stream<FlutterEngineRevision> _logEngineVersions() async* {
  final ProcessResult result = await Process.run(
    'git',
    <String>[
      'log',
      '--oneline',
      '-p',
      '--',
      'bin/internal/engine.version',
    ],
  );
  if (result.exitCode != 0) {
    print(result.stderr);
    throw Exception('Failed to log bin/internal/engine.version');
  }

  final List<String> lines = (result.stdout as String).split('\n');
  int index = 0;
  while (index < lines.length - 1) {
    final String flutterRevision = lines[index].split(' ').first;
    index += 1;
    while (!lines[index].startsWith('+') || lines[index].startsWith('+++')) {
      index += 1;
    }
    if (index >= lines.length) {
      break;
    }
    final String engineRevision = lines[index].substring(1);
    yield FlutterEngineRevision(flutterRevision, engineRevision);
    index += lines[index + 1].startsWith(r'\ ') ? 2 : 1;
  }
}

class FlutterEngineRevision {
  const FlutterEngineRevision(this.flutterRevision, this.engineRevision);

  final String flutterRevision;
  final String engineRevision;

  @override
  String toString() => '$flutterRevision: $engineRevision';
}
