// Copyright (c) 2017, 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:async';
import 'dart:convert';

import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/plugin/plugin_isolate.dart';
import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;

mixin RequestHandlerMixin<T extends AnalysisServer> {
  /// The analysis server that is using this handler to process requests.
  T get server;

  /// Given a mapping from plugins to futures that will complete when the plugin
  /// has responded to a request, wait for a finite amount of time for each of
  /// the plugins to respond. Return a list of the responses from each of the
  /// plugins. If a plugin fails to return a response, notify the plugin manager
  /// associated with the server so that non-responsive plugins can be killed or
  /// restarted. The [timeout] is the maximum amount of time that will be spent
  /// waiting for plugins to respond.
  Future<List<plugin.Response>> waitForResponses(
    Map<PluginIsolate, Future<plugin.Response>> futures, {
    plugin.RequestParams? requestParameters,
    Duration timeout = const Duration(milliseconds: 500),
  }) async {
    // TODO(brianwilkerson): requestParameters might need to be required.
    var timer = Stopwatch()..start();
    var responses = <plugin.Response>[];
    for (var entry in futures.entries) {
      var pluginInfo = entry.key;
      var future = entry.value;
      try {
        var response = await future.timeout(timeout - timer.elapsed);
        var error = response.error;
        if (error != null) {
          // TODO(brianwilkerson): Report the error to the plugin manager.
          server.instrumentationService.logPluginError(
            pluginInfo.data,
            error.code.name,
            error.message,
            error.stackTrace ?? StackTrace.current.toString(),
          );
        } else {
          responses.add(response);
        }
      } on TimeoutException {
        // TODO(brianwilkerson): Report the timeout to the plugin manager.
        server.instrumentationService.logPluginTimeout(
          pluginInfo.data,
          JsonEncoder().convert(
            requestParameters?.toRequest('-').toJson() ?? {},
          ),
        );
      } catch (exception, stackTrace) {
        // TODO(brianwilkerson): Report the exception to the plugin manager.
        server.instrumentationService.logPluginException(
          pluginInfo.data,
          exception,
          stackTrace,
        );
      }
    }
    return responses;
  }
}
