// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
// for details. 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';

/// Calculates the amount of formatting changes in a given directory of code.
///
/// This should be run with a path to a directory. That directory should
/// contain a Git repo. The committed state of the repo should be the formatted
/// output before the change in question. Then the result of the new formatting
/// should be unstaged changes.
///
/// Uses `git diff --shortstat` to calculate the number of changed lines.
///
/// Counts the number of lines of Dart code by reading the files.
void main(List<String> arguments) {
  if (arguments.length != 1) {
    print('Usage: change_metrics.dart <dir>');
    exit(1);
  }

  var directory = arguments[0];
  var totalLines = 0;
  var totalFiles = 0;

  print('Counting lines...');
  for (var entry in Directory(directory).listSync(recursive: true)) {
    if (entry is File && entry.path.endsWith('.dart')) {
      try {
        var lines = entry.readAsLinesSync();
        totalFiles++;
        totalLines += lines.length;
      } catch (error) {
        print('Could not read ${entry.path}:\n$error');
      }
    }
  }

  print('Getting diff stats...');
  var result = Process.runSync(
    'git',
    ['diff', '--shortstat'],
    // Make sure the user's local Git config doesn't affect the output.
    environment: {'GIT_CONFIG_NOGLOBAL': 'true', 'GIT_CONFIG_NOSYSTEM': 'true'},
    workingDirectory: directory,
  );
  if (result.exitCode != 0) {
    print('Git failure:\n${result.stdout}\n${result.stderr}');
    exit(1);
  }

  var stdout = result.stdout as String;
  var insertions = _parseGitStdout(stdout, r'(\d+) insertions');
  var deletions = _parseGitStdout(stdout, r'(\d+) deletions');
  var changes = insertions + deletions;

  print('$totalLines lines in $totalFiles files');
  print('$insertions insertions + $deletions deletions = $changes changes');
  var linesPerChange = totalLines / changes;
  print(
    '1 changed line for every ${linesPerChange.toStringAsFixed(2)} '
    'lines of code',
  );

  var changesPerLine = 1000.0 * changes / totalLines;
  print(
    '${changesPerLine.toStringAsFixed(4)} '
    'changed lines for every 1,000 lines of code',
  );
}

int _parseGitStdout(String stdout, String pattern) {
  var match = RegExp(pattern).firstMatch(stdout);
  if (match == null) {
    print('Could not parse Git output:\n$stdout');
    exit(1);
  }

  return int.parse(match[1]!);
}
