// 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:async';

import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
import 'package:analysis_server/src/lsp/constants.dart';
import 'package:analysis_server/src/lsp/error_or.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart' as lsp;
import 'package:analyzer/dart/analysis/session.dart';
import 'package:language_server_protocol/json_parsing.dart';
import 'package:language_server_protocol/protocol_custom_generated.dart';
import 'package:language_server_protocol/protocol_generated.dart';
import 'package:language_server_protocol/protocol_special.dart';

/// The handler for the `lsp.handle` request.
class LspOverLegacyHandler extends LegacyHandler {
  new(super.server, super.request, super.cancellationToken, super.performance);

  @override
  bool get recordsOwnAnalytics => true;

  @override
  Future<void> handle() async {
    server.initializeLspOverLegacy();

    var params = LspHandleParams.fromRequest(
      request,
      clientUriConverter: server.uriConverter,
    );
    var lspMessageJson = params.lspMessage;
    var reporter = LspJsonReporter();
    var lspMessage =
        lspMessageJson is Map<String, Object?> &&
            RequestMessage.canParse(lspMessageJson, reporter)
        ? RequestMessage.fromJson({
            // Pass across any clientRequestTime from the envelope so that we
            // can record latency for LSP-over-Legacy requests.
            'clientRequestTime': request.clientRequestTime,
            ...lspMessageJson,
          })
        : null;

    if (lspMessage != null) {
      server.analyticsManager.startedRequestMessage(
        request: lspMessage,
        startTime: DateTime.now(),
      );
      await handleRequest(lspMessage);
    } else {
      var message =
          "The 'lspMessage' parameter was not a valid LSP request:\n"
          "${reporter.errors.join('\n')}";
      var error = RequestError(RequestErrorCode.INVALID_PARAMETER, message);
      sendResponse(Response(request.id, error: error));
    }
  }

  Future<void> handleRequest(RequestMessage message) async {
    var messageInfo = lsp.MessageInfo(
      performance: performance,
      clientCapabilities: server.editorClientCapabilities,
      timeSinceRequest: request.timeSinceRequest,
      isTrustedCaller: true,
    );

    ErrorOr<Object?> result;
    try {
      // Since this (legacy) request was already scheduled, we immediately
      // execute the wrapped LSP request without additional scheduling.
      result = await server.immediatelyHandleLspMessage(
        message,
        messageInfo,
        cancellationToken: cancellationToken,
      );
    } on InconsistentAnalysisException {
      result = error(
        ErrorCodes.ContentModified,
        'Document was modified before operation completed',
      );
    } catch (e) {
      var errorMessage =
          'An error occurred while handling ${message.method} request: $e';
      result = error(ServerErrorCodes.unhandledError, errorMessage);
    }

    var lspResponse = ResponseMessage(
      id: message.id,
      error: result.errorOrNull,
      result: result.resultOrNull,
      jsonrpc: jsonRpcVersion,
    );

    server.analyticsManager.sentResponseMessage(response: lspResponse);
    sendResult(LspHandleResult(lspResponse.toJson()));
  }
}
