// Copyright (c) 2026, 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:_embedder' as embedder;
import 'dart:_internal' show patch;
import 'dart:_string' show embedderStringFromDartString;
import 'dart:_wasm';
import 'dart:async' show Zone;
import 'dart:isolate';

@patch
bool debugger({bool when = true, String? message}) {
  if (when) {
    embedder.debugger(
      message == null
          ? WasmExternRef.nullRef
          : embedderStringFromDartString(message).wrappedExternRef,
    );
  }

  return when;
}

@patch
Object? inspect(Object? object) {
  embedder.inspect(object == null ? null : WasmAnyRef.fromObject(object));
  return object;
}

@patch
void log(
  String message, {
  DateTime? time,
  int? sequenceNumber,
  int level = 0,
  String name = '',
  Zone? zone,
  Object? error,
  StackTrace? stackTrace,
}) {
  // TODO.
}

@patch
int get reachabilityBarrier => 0;

@patch
abstract final class NativeRuntime {
  @patch
  static String? get buildId => null;

  @patch
  static void writeHeapSnapshotToFile(String filepath) =>
      throw UnsupportedError(
        "Generating heap snapshots is not supported for WebAssembly.",
      );

  @patch
  static void streamTimelineTo(
    TimelineRecorder recorder, {
    String? path,
    String streams = "Dart,GC,Compiler",
    bool enableProfiler = false,
    Duration samplingInterval = const Duration(microseconds: 1000),
  }) => throw UnsupportedError(
    "Streaming timelines is not supported for WebAssembly.",
  );

  @patch
  static void stopStreamingTimeline() => throw UnsupportedError(
    "Streaming timelines is not supported for WebAssembly.",
  );
}

@patch
bool get extensionStreamHasListener => false;

final _extensions = <String, ServiceExtensionHandler>{};

@patch
void _postEvent(String eventKind, String eventData) {
  // TODO.
}

@patch
ServiceExtensionHandler? _lookupExtension(String method) {
  return _extensions[method];
}

@patch
_registerExtension(String method, ServiceExtensionHandler handler) {
  _extensions[method] = handler;
}

@patch
void _getServerInfo(SendPort sendPort) {
  sendPort.send(null);
}

@patch
void _webServerControl(SendPort sendPort, bool enable, bool? silenceOutput) {
  sendPort.send(null);
}

@patch
int _getServiceMajorVersion() {
  return 0;
}

@patch
int _getServiceMinorVersion() {
  return 0;
}

@patch
String? _getIsolateIdFromSendPort(SendPort sendPort) {
  return null;
}

@patch
String? _getObjectId(Object object) {
  return null;
}

@patch
class UserTag {
  @patch
  factory UserTag(String label) = _FakeUserTag;

  @patch
  static UserTag get defaultTag => _FakeUserTag._defaultTag;
}

final class _FakeUserTag implements UserTag {
  static final _instances = <String, _FakeUserTag>{};

  _FakeUserTag.real(this.label);

  factory _FakeUserTag(String label) {
    // Canonicalize by name.
    var existingTag = _instances[label];
    if (existingTag != null) {
      return existingTag;
    }
    // Throw an exception if we've reached the maximum number of user tags.
    if (_instances.length == UserTag.maxUserTags) {
      throw UnsupportedError(
        'UserTag instance limit (${UserTag.maxUserTags}) reached.',
      );
    }
    return _instances[label] = _FakeUserTag.real(label);
  }

  final String label;

  UserTag makeCurrent() {
    var old = _currentTag;
    _currentTag = this;
    return old;
  }

  static final UserTag _defaultTag = _FakeUserTag('Default');
}

var _currentTag = _FakeUserTag._defaultTag;

@patch
UserTag getCurrentTag() => _currentTag;

@patch
bool _isDartStreamEnabled() => embedder.dartTimelineStreamEnabled().toBool();

int _taskId = 1;

@patch
int _getNextTaskId() {
  return _taskId++;
}

@patch
int _getTraceClock() => embedder.monotonicClockTicks().toInt();

@patch
void _reportTaskEvent(
  int taskId,
  int flowId,
  int type,
  String name,
  String argumentsAsJson,
) {
  embedder.reportTaskEvent(
    WasmI32.fromInt(taskId),
    WasmI32.fromInt(flowId),
    WasmI32.fromInt(type),
    embedderStringFromDartString(name).wrappedExternRef,
    embedderStringFromDartString(argumentsAsJson).wrappedExternRef,
  );
}
