import 'dart:convert';
import 'dart:io';

/// Simple analyzer for dart2js dump-info output.
/// Usage: dart run tools/print_size_info.dart <path_to_info_json> [topN]
void main(List<String> args) {
  if (args.isEmpty) {
    stderr.writeln(
      'Usage: dart run tools/print_size_info.dart <path_to_info_json> [topN]',
    );
    exit(1);
  }

  final infoFile = File(args[0]);
  if (!infoFile.existsSync()) {
    stderr.writeln('File not found: ${infoFile.path}');
    exit(1);
  }

  final topN = args.length > 1 ? int.tryParse(args[1]) ?? 20 : 20;
  final root = json.decode(infoFile.readAsStringSync()) as Map<String, dynamic>;
  final libraries = <Map<String, dynamic>>[];

  // Newer dump-info format exposes elements in root['elements']['library'].
  final elements = root['elements'];
  if (elements is Map && elements['library'] is Map) {
    final libs = elements['library'] as Map;
    libs.forEach((_, value) {
      if (value is Map<String, dynamic>) {
        final uri = value['canonicalUri']?.toString() ?? '';
        final displayName = uri.startsWith('package:')
            ? uri
            : (value['name']?.toString().isNotEmpty ?? false
                ? value['name'].toString()
                : uri.isNotEmpty
                    ? uri
                    : '<unknown>');
        final size = value['size'];
        libraries.add({
          'name': displayName,
          'size': size is num ? size : num.tryParse('$size') ?? 0,
        });
      }
    });
  } else {
    // Fallback to old format by walking the program tree.
    final program = root['program'];
    if (program is! Map<String, dynamic>) {
      stderr.writeln('Invalid dump-info format: missing program node');
      exit(1);
    }

    void walk(Map<String, dynamic> node) {
      if (node['kind'] == 'library') {
        libraries.add({'name': node['name'], 'size': node['size']});
      }
      final children = node['children'];
      if (children is List) {
        for (final child in children) {
          if (child is Map<String, dynamic>) {
            walk(child);
          }
        }
      }
    }

    walk(program);
  }
  libraries.sort(
    (a, b) => (b['size'] as num).compareTo(a['size'] as num),
  );

  if (libraries.isEmpty) {
    stdout.writeln('No libraries found in info file. Structure may differ.');
    return;
  }

  stdout.writeln('Top $topN libraries by size (bytes):');
  for (final entry in libraries.take(topN)) {
    final size = (entry['size'] as num).toString().padLeft(10);
    stdout.writeln('$size  ${entry['name']}');
  }
}
