// Copyright (c) 2014, 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 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import 'analysis_server_base.dart';
import 'mocks.dart';

void main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(ExecutionDomainTest);
  });
  // group('ExecutionDomainHandler', () {
  //   var provider = MemoryResourceProvider();
  //   late AnalysisServer server;
  //   late ExecutionDomainHandler handler;
  //
  //   setUp(() {
  //     server = AnalysisServer(
  //         MockServerChannel(),
  //         provider,
  //         AnalysisServerOptions(),
  //         DartSdkManager(''),
  //         CrashReportingAttachmentsBuilder.empty,
  //         InstrumentationService.NULL_SERVICE);
  //     handler = ExecutionDomainHandler(server, server.executionContext);
  //   });
  //
  //  // `TODO`(brianwilkerson) Re-enable these tests if we re-enable the
  //  // execution.mapUri request.
  //    group('mapUri', () {
  //      String contextId;
  //
  //      void createExecutionContextIdForFile(String path) {
  //        Request request = new ExecutionCreateContextParams(path).toRequest('0', clientUriConverter: server.uriConverter);
  //        Response response = handler.handleRequest(request, NotCancelableToken());
  //        expect(response, isResponseSuccess('0'));
  //        ExecutionCreateContextResult result =
  //            new ExecutionCreateContextResult.fromResponse(response, clientUriConverter: server.uriConverter);
  //        contextId = result.id;
  //      }
  //
  //      setUp(() {
  //        Folder folder = provider.newFile('/a/b.dart', '').parent;
  //        server.folderMap.putIfAbsent(folder, () {
  //          SourceFactory factory =
  //              new SourceFactory([new ResourceUriResolver(provider)]);
  //          AnalysisContext context =
  //              AnalysisEngine.instance.createAnalysisContext();
  //          context.sourceFactory = factory;
  //          return context;
  //        });
  //        createExecutionContextIdForFile('/a/b.dart');
  //      });
  //
  //      tearDown(() {
  //        Request request =
  //            new ExecutionDeleteContextParams(contextId).toRequest('1', clientUriConverter: server.uriConverter);
  //        Response response = handler.handleRequest(request, NotCancelableToken());
  //        expect(response, isResponseSuccess('1'));
  //      });
  //
  //      group('file to URI', () {
  //        test('does not exist', () {
  //          Request request =
  //              new ExecutionMapUriParams(contextId, file: '/a/c.dart')
  //                  .toRequest('2', clientUriConverter: server.uriConverter);
  //          Response response = handler.handleRequest(request, NotCancelableToken());
  //          expect(response, isResponseFailure('2'));
  //        });
  //
  //        test('directory', () {
  //          provider.newFolder('/a/d');
  //          Request request =
  //              new ExecutionMapUriParams(contextId, file: '/a/d').toRequest('2', clientUriConverter: server.uriConverter);
  //          Response response = handler.handleRequest(request, NotCancelableToken());
  //          expect(response, isResponseFailure('2'));
  //        });
  //      });
  //
  //      group('URI to file', () {
  //        test('invalid', () {
  //          Request request =
  //              new ExecutionMapUriParams(contextId, uri: 'foo:///a/b.dart')
  //                  .toRequest('2', clientUriConverter: server.uriConverter);
  //          Response response = handler.handleRequest(request, NotCancelableToken());
  //          expect(response, isResponseFailure('2'));
  //        });
  //      });
  //
  //      test('invalid context id', () {
  //        Request request =
  //            new ExecutionMapUriParams('xxx', uri: '').toRequest('4', clientUriConverter: server.uriConverter);
  //        Response response = handler.handleRequest(request, NotCancelableToken());
  //        expect(response, isResponseFailure('4'));
  //      });
  //
  //      test('both file and uri', () {
  //        Request request =
  //            new ExecutionMapUriParams('xxx', file: '', uri: '').toRequest('5', clientUriConverter: server.uriConverter);
  //        Response response = handler.handleRequest(request, NotCancelableToken());
  //        expect(response, isResponseFailure('5'));
  //      });
  //
  //      test('neither file nor uri', () {
  //        Request request = new ExecutionMapUriParams('xxx').toRequest('6');
  //        Response response = handler.handleRequest(request, NotCancelableToken());
  //        expect(response, isResponseFailure('6'));
  //      });
  //    });
  //   });
}

@reflectiveTest
class ExecutionDomainTest extends PubPackageAnalysisServerTest {
  late String contextId;

  @override
  Future<void> setUp() async {
    super.setUp();
    await setRoots(included: [workspaceRootPath], excluded: []);
    await _createExecutionContext(testFile);
  }

  @override
  Future<void> tearDown() async {
    await _disposeExecutionContext();
    await super.tearDown();
  }

  Future<void> test_createAndDeleteMultipleContexts() async {
    var request = ExecutionCreateContextParams(
      '/a/b.dart',
    ).toRequest('0', clientUriConverter: server.uriConverter);
    var response = await handleSuccessfulRequest(request);
    expect(response, isResponseSuccess('0'));
    var result = ExecutionCreateContextResult.fromResponse(
      response,
      clientUriConverter: server.uriConverter,
    );
    var id0 = result.id;

    request = ExecutionCreateContextParams(
      '/c/d.dart',
    ).toRequest('1', clientUriConverter: server.uriConverter);
    response = await handleSuccessfulRequest(request);
    expect(response, isResponseSuccess('1'));
    result = ExecutionCreateContextResult.fromResponse(
      response,
      clientUriConverter: server.uriConverter,
    );
    var id1 = result.id;

    expect(id0 == id1, isFalse);

    request = ExecutionDeleteContextParams(
      id0,
    ).toRequest('2', clientUriConverter: server.uriConverter);
    response = await handleSuccessfulRequest(request);
    expect(response, isResponseSuccess('2'));

    request = ExecutionDeleteContextParams(
      id1,
    ).toRequest('3', clientUriConverter: server.uriConverter);
    response = await handleSuccessfulRequest(request);
    expect(response, isResponseSuccess('3'));
  }

  Future<void> test_deleteNonExistentContext() async {
    var request = ExecutionDeleteContextParams(
      '13',
    ).toRequest('0', clientUriConverter: server.uriConverter);
    var response = await handleSuccessfulRequest(request);
    // TODO(brianwilkerson): It isn't currently specified to be an error if a
    // client attempts to delete a context that doesn't exist. Should it be?
    //        expect(response, isResponseFailure('0'));
    expect(response, isResponseSuccess('0'));
  }

  Future<void> test_getSuggestions() async {
    var code = r'''
class A {
  int foo;
}

void contextFunction() {
  var a = A();
  // context line
}
''';

    var path = newFile('/test.dart', '').path;
    newFile(path, code);

    var request = ExecutionGetSuggestionsParams(
      'a.',
      2,
      path,
      code.indexOf('// context line'),
      <RuntimeCompletionVariable>[],
    ).toRequest('0', clientUriConverter: server.uriConverter);
    var response = await handleSuccessfulRequest(request);

    var result = ExecutionGetSuggestionsResult.fromResponse(
      response,
      clientUriConverter: server.uriConverter,
    );
    //    expect(result.suggestions, isNotEmpty);
    //
    //    expect(
    //        result.suggestions,
    //        contains(
    //            predicate<CompletionSuggestion>((s) => s.completion == 'foo')));

    // TODO(brianwilkerson): Restore the expectations above (and delete the line
    // below) after the functionality has been re-enabled.
    expect(result.suggestions, isEmpty);
  }

  Future<void> test_mapUri_file() async {
    var file = newFile('/a/b.dart', '');
    // map the file
    var result = await _mapUri(file: file.path);
    expect(result.file, isNull);
    expect(result.uri, file.toUri().toString());
  }

  Future<void> test_mapUri_file_dartUriKind() async {
    var path = (await _mapUri(uri: 'dart:async')).file;
    // map file
    var result = await _mapUri(file: path);
    expect(result.file, isNull);
    expect(result.uri, 'dart:async');
  }

  Future<void> test_mapUri_uri() async {
    var file = newFile('/a/b.dart', '');
    // map the uri
    var result = await _mapUri(uri: file.toUri().toString());
    expect(result.file, file.path);
    expect(result.uri, isNull);
  }

  Future<void> _createExecutionContext(File file) async {
    var request = ExecutionCreateContextParams(
      file.path,
    ).toRequest('0', clientUriConverter: server.uriConverter);
    var response = await handleSuccessfulRequest(request);
    expect(response, isResponseSuccess('0'));
    var result = ExecutionCreateContextResult.fromResponse(
      response,
      clientUriConverter: server.uriConverter,
    );
    contextId = result.id;
  }

  Future<void> _disposeExecutionContext() async {
    var request = ExecutionDeleteContextParams(
      contextId,
    ).toRequest('1', clientUriConverter: server.uriConverter);
    var response = await handleSuccessfulRequest(request);
    expect(response, isResponseSuccess('1'));
  }

  Future<ExecutionMapUriResult> _mapUri({String? file, String? uri}) async {
    var request = ExecutionMapUriParams(
      contextId,
      file: file,
      uri: uri,
    ).toRequest('2', clientUriConverter: server.uriConverter);
    var response = await handleSuccessfulRequest(request);
    expect(response, isResponseSuccess('2'));
    return ExecutionMapUriResult.fromResponse(
      response,
      clientUriConverter: server.uriConverter,
    );
  }
}
