// Copyright (c) 2022, 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/lsp_protocol/protocol.dart';
import 'package:analysis_server/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generator.dart';
import 'package:analyzer/source/file_source.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:analyzer/src/pubspec/pubspec_validator.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:yaml/yaml.dart';

/// Produces [CodeActionLiteral]s from Pubspec fixes.
class PubspecCodeActionsProducer extends AbstractCodeActionsProducer {
  new(
    super.server,
    super.file,
    super.lineInfo, {
    required super.offset,
    required super.length,
    required super.shouldIncludeKind,
    required super.editorCapabilities,
    required super.callerCapabilities,
    required super.allowCodeActionLiterals,
    required super.allowCommands,
    required super.analysisOptions,
    required super.allowSnippets,
  });

  @override
  String get name => 'ServerPubspecActionsComputer';

  @override
  Future<List<CodeActionWithPriority>> getAssistActions({
    OperationPerformance? performance,
  }) async => [];

  @override
  Future<List<CodeActionWithPriority>> getFixActions(
    OperationPerformance? performance,
  ) async {
    // These fixes are only provided as literal CodeActions.
    if (!allowCodeActionLiterals) {
      // TODO(dantup): Support this (via createCodeActionLiteralOrApplyCommand)
      return [];
    }

    var session = await server.getAnalysisSession(path);
    if (session == null) {
      return [];
    }

    var resourceProvider = server.resourceProvider;
    var pubspecFile = resourceProvider.getFile(path);
    var content = safelyRead(pubspecFile);
    if (content == null) {
      return [];
    }
    var lineInfo = LineInfo.fromContent(content);

    YamlNode node;
    try {
      node = loadYamlNode(content, sourceUrl: file.toUri());
    } catch (exception) {
      return [];
    }
    var errors = validatePubspec(
      contents: node,
      source: FileSource(pubspecFile),
      provider: resourceProvider,
      analysisOptions: analysisOptions,
    );

    var codeActions = <CodeActionWithPriority>[];
    for (var error in errors) {
      var generator = PubspecFixGenerator(
        resourceProvider,
        error,
        content,
        node,
        defaultEol: server.defaultEol,
      );
      var fixes = await generator.computeFixes();
      if (fixes.isEmpty) {
        continue;
      }

      var result = createResult(session, lineInfo, errors);
      var diagnostic = createDiagnostic(lineInfo, result, error);
      codeActions.addAll(
        fixes.map((fix) {
          var kind = toCodeActionKind(fix.change.id, CodeActionKind.QuickFix);
          // TODO(dantup): Find a way to filter these earlier, so we don't
          //  compute fixes we will filter out.
          if (!shouldIncludeKind(kind)) {
            return null;
          }
          var action = CodeAction.t1(
            createCodeActionLiteral(
              fix.change,
              kind,
              fix.change.id,
              path,
              lineInfo,
              diagnostic: diagnostic,
            ),
          );
          return (action: action, priority: fix.kind.priority);
        }).nonNulls,
      );
    }

    return codeActions;
  }

  @override
  Future<List<CodeAction>> getRefactorActions(
    OperationPerformance? performance,
  ) async => [];

  @override
  Future<List<CodeAction>> getSourceActions() async => [];
}
