// Copyright (c) 2025, 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:developer';

import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart';

import 'common/service_test_common.dart';
import 'common/test_helper.dart';

// AUTOGENERATED START
//
// Update these constants by running:
//
// dart pkg/vm_service/test/update_line_numbers.dart pkg/vm_service/test/coverage_extension_methods_test.dart
//
const OFFSET_GET_FROM = 0782;
const OFFSET_BAZ_START = 0830;
const OFFSET_PRINT = 0866;
const OFFSET_BAZ_END = 0905;
const OFFSET_GET_TO = 0928;
// AUTOGENERATED END

class Bar {}

/* OFFSET_GET_FROM */ extension Foo on Bar {
  /* OFFSET_BAZ_START */ void baz() {
    /* OFFSET_PRINT */ print('hello'); /* OFFSET_BAZ_END */
  }
} /* OFFSET_GET_TO */

void testFunction() {
  debugger();
  final bar = Bar();
  bar.baz();
  debugger();
}

Future<({Set<int> hits, Set<int> misses})> verifyLocationAndGetHitsAndMisses(
  VmService service,
  IsolateRef isolateRef,
) async {
  final isolateId = isolateRef.id!;
  final stack = await service.getStack(isolateId);

  // Make sure we are in the right place.
  final frames = stack.frames!;
  expect(frames.length, greaterThanOrEqualTo(1));
  expect(frames[0].function!.name, 'testFunction');

  final location = frames[0].function!.location!;
  final report = await service.getSourceReport(
    isolateId,
    [SourceReportKind.kCoverage],
    scriptId: location.script!.id!,
    tokenPos: OFFSET_GET_FROM,
    endTokenPos: OFFSET_GET_TO,
    forceCompile: true,
  );

  final scripts = report.scripts!;
  final ranges = report.ranges!;
  final hits = <int>{};
  final misses = <int>{};
  // We have more ranges because there are several methods:
  // The actual method, a tearoff and a closure inside the tearoff.
  for (final range in ranges) {
    hits.addAll(range.coverage!.hits!);
    misses.addAll(range.coverage!.misses!);
    expect(range.startPos, OFFSET_BAZ_START);
    expect(range.endPos, OFFSET_BAZ_END);
    expect(range.scriptIndex, 0);
  }
  misses.removeAll(hits);
  expect(
    scripts[0].uri,
    endsWith('coverage_extension_methods_test.dart'),
  );
  return (hits: hits, misses: misses);
}

var tests = <IsolateTest>[
  hasStoppedAtBreakpoint,
  (VmService service, IsolateRef isolateRef) async {
    final (:hits, :misses) =
        await verifyLocationAndGetHitsAndMisses(service, isolateRef);
    expect(hits, isEmpty);
    expect(misses, {OFFSET_BAZ_START, OFFSET_PRINT});
  },
  resumeIsolate,
  hasStoppedAtBreakpoint,
  (VmService service, IsolateRef isolateRef) async {
    final (:hits, :misses) =
        await verifyLocationAndGetHitsAndMisses(service, isolateRef);
    expect(hits, {OFFSET_BAZ_START, OFFSET_PRINT});
    expect(misses, isEmpty);
  },
];

void main(List<String> args) => runIsolateTests(
      args,
      tests,
      'coverage_extension_methods_test.dart',
      testeeConcurrent: testFunction,
    );
