// 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 'package:analysis_server/src/services/correction/assist.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

import 'assist_processor.dart';

void main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(ConvertToInitializingFormalDifferentTypesTest);
    defineReflectiveTests(ConvertToInitializingFormalLocationTest);
    defineReflectiveTests(ConvertToInitializingFormalOtherTest);
    defineReflectiveTests(ConvertToInitializingFormalPrivateTest);
    defineReflectiveTests(ConvertToInitializingFormalRenameTest);
  });
}

/// Test that if the parameter and field have different types then the parameter
/// type is retained.
@reflectiveTest
class ConvertToInitializingFormalDifferentTypesTest
    extends AssistProcessorTest {
  @override
  AssistKind get kind => DartAssistKind.convertToInitializingFormal;

  Future<void> test_primary_assignment() async {
    await resolveTestCode('''
class C(String ^a) {
  Object a = '';
  this {
    this.a = a;
  }
}
''');
    await assertHasAssist('''
class C(String this.a) {
  Object a = '';
  this;
}
''');
  }

  Future<void> test_primary_initializer() async {
    await resolveTestCode('''
class C(String ^a) {
  Object a;
  this : a = a;
}
''');
    await assertHasAssist('''
class C(String this.a) {
  Object a;
  this;
}
''');
  }

  Future<void> test_primary_optionalNamed() async {
    await resolveTestCode('''
class C({String? ^a}) {
  Object? a;
  this {
    this.a = a;
  }
}
''');
    await assertHasAssist('''
class C({String? this.a}) {
  Object? a;
  this;
}
''');
  }

  Future<void> test_primary_optionalPositional() async {
    await resolveTestCode('''
class C([String? ^a]) {
  Object? a;
  this {
    this.a = a;
  }
}
''');
    await assertHasAssist('''
class C([String? this.a]) {
  Object? a;
  this;
}
''');
  }

  Future<void> test_secondary_assignment() async {
    await resolveTestCode('''
class C {
  Object a = '';
  C(String ^a) {
    this.a = a;
  }
}
''');
    await assertHasAssist('''
class C {
  Object a = '';
  C(String this.a);
}
''');
  }

  Future<void> test_secondary_initializer() async {
    await resolveTestCode('''
class C {
  Object a = '';
  C(String ^a) : a = a;
}
''');
    await assertHasAssist('''
class C {
  Object a = '';
  C(String this.a);
}
''');
  }

  Future<void> test_secondary_optionalNamed() async {
    await resolveTestCode('''
class C {
  Object? a;
  C({String? ^a}) {
    this.a = a;
  }
}
''');
    await assertHasAssist('''
class C {
  Object? a;
  C({String? this.a});
}
''');
  }

  Future<void> test_secondary_optionalPositional() async {
    await resolveTestCode('''
class C {
  Object? a;
  C([String? ^a]) {
    this.a = a;
  }
}
''');
    await assertHasAssist('''
class C {
  Object? a;
  C([String? this.a]);
}
''');
  }
}

/// Tests where the cursor can and can't be while triggering the assist.
@reflectiveTest
class ConvertToInitializingFormalLocationTest extends AssistProcessorTest {
  @override
  AssistKind get kind => DartAssistKind.convertToInitializingFormal;

  Future<void> test_assignment_leftSide() async {
    // This would be useful to support, but isn't currently handled.
    await resolveTestCode('''
class A {
  int? aaa;
  A(int? aaa) {
    this.^aaa = aaa;
  }
}
''');
    await assertNoAssist();
  }

  Future<void> test_assignment_rightSide_primary() async {
    await resolveTestCode('''
class C(int? aaa) {
  int? aaa;
  this {
    this.aaa = ^aaa;
  }
}
''');
    await assertHasAssist('''
class C(this.aaa) {
  int? aaa;
  this;
}
''');
  }

  Future<void> test_assignment_rightSide_secondary() async {
    await resolveTestCode('''
class A {
  int? aaa;
  A(int? aaa) {
    this.aaa = ^aaa;
  }
}
''');
    await assertHasAssist('''
class A {
  int? aaa;
  A(this.aaa);
}
''');
  }

  Future<void> test_fieldDeclaration() async {
    // This would be useful to support, but isn't currently handled.
    await resolveTestCode('''
class A {
  int? ^test;
  A(int? test) : test = test;
}
''');
    await assertNoAssist();
  }

  Future<void> test_initializer_leftSide() async {
    // This would be useful to support, but isn't currently handled.
    await resolveTestCode('''
class A {
  int? aaa;
  A(int? aaa) : ^aaa = aaa;
}
''');
    await assertNoAssist();
  }

  Future<void> test_initializer_rightSide_explicitThis() async {
    await resolveTestCode('''
class A {
  int? aaa;
  A(int? aaa) : this.aaa = ^aaa;
}
''');
    await assertHasAssist('''
class A {
  int? aaa;
  A(this.aaa);
}
''');
  }

  Future<void> test_initializer_rightSide_primary() async {
    await resolveTestCode('''
class C(int? aaa) {
  int? aaa;
  this : aaa = ^aaa;
}
''');
    await assertHasAssist('''
class C(this.aaa) {
  int? aaa;
  this;
}
''');
  }

  Future<void> test_initializer_rightSide_secondary() async {
    await resolveTestCode('''
class A {
  int? aaa;
  A(int? aaa) : aaa = ^aaa;
}
''');
    await assertHasAssist('''
class A {
  int? aaa;
  A(this.aaa);
}
''');
  }

  Future<void> test_parameterDeclaration_primary_declaring_final() async {
    await resolveTestCode('''
class C(final int ^test);
''');
    await assertHasAssist('''
class C(this.test) {
  final int test;
}
''');
  }

  Future<void>
  test_parameterDeclaration_primary_declaring_functionTyped() async {
    await resolveTestCode('''
class C(var int ^test(String));
''');
    await assertHasAssist('''
class C(this.test) {
  int Function(String) test;
}
''');
  }

  Future<void> test_parameterDeclaration_primary_declaring_var() async {
    await resolveTestCode('''
class C(var int ^test);
''');
    await assertHasAssist('''
class C(this.test) {
  int test;
}
''');
  }

  Future<void> test_parameterDeclaration_primary_regular() async {
    await resolveTestCode('''
class C(int ^test) {
  int test;
  this : test = test;
}
''');
    await assertHasAssist('''
class C(this.test) {
  int test;
  this;
}
''');
  }

  Future<void> test_parameterDeclaration_secondary() async {
    await resolveTestCode('''
class A {
  int test;
  A(int ^test) : test = test;
}
''');
    await assertHasAssist('''
class A {
  int test;
  A(this.test);
}
''');
  }
}

@reflectiveTest
class ConvertToInitializingFormalOtherTest extends AssistProcessorTest {
  @override
  AssistKind get kind => DartAssistKind.convertToInitializingFormal;

  Future<void> test_assignment_emptyAfterRemoval() async {
    await resolveTestCode('''
class C {
  int a = 0;
  C(int ^a) {
    this.a = a;
  }
}
''');
    await assertHasAssist('''
class C {
  int a = 0;
  C(this.a);
}
''');
  }

  Future<void> test_assignment_notEmptyAfterRemoval() async {
    await resolveTestCode('''
class C {
  int a = 0;
  C(int ^a) {
    this.a = a;
    print(1);
  }
}
''');
    await assertHasAssist('''
class C {
  int a = 0;
  C(this.a) {
    print(1);
  }
}
''');
  }

  Future<void> test_assignment_notSimple() async {
    await resolveTestCode('''
class A {
  int? aaa;
  A(int? ^aaa) {
    this.aaa = aaa ?? 2;
  }
}
''');
    await assertNoAssist();
  }

  Future<void> test_bothInitializerAndAssignment() async {
    await resolveTestCode('''
class A {
  int aaa;
  A(int ^aaa) : aaa = aaa {
    this.aaa = aaa;
  }
}
''');
    await assertHasAssist('''
class A {
  int aaa;
  A(this.aaa) {
    this.aaa = aaa;
  }
}
''');
  }

  Future<void> test_initializer_emptyAfterRemoval() async {
    await resolveTestCode('''
class C {
  int a;
  C(int ^a) : this.a = a;
}
''');
    await assertHasAssist('''
class C {
  int a;
  C(this.a);
}
''');
  }

  Future<void> test_initializer_notEmptyAfterRemoval() async {
    await resolveTestCode('''
class C {
  int a;
  int b;
  C(int ^a) : this.a = a, this.b = 2;
}
''');
    await assertHasAssist('''
class C {
  int a;
  int b;
  C(this.a) : this.b = 2;
}
''');
  }

  Future<void> test_initializer_notSimple() async {
    await resolveTestCode('''
class A {
  int aaa;
  A(int ^aaa) : aaa = aaa * 2;
}
''');
    await assertNoAssist();
  }

  Future<void> test_multipleInitializers_first() async {
    await resolveTestCode('''
class A {
  int aaa2;
  int bbb2;
  A(int ^aaa, int bbb) : aaa2 = aaa, bbb2 = bbb;
}
''');
    await assertHasAssist('''
class A {
  int aaa2;
  int bbb2;
  A(this.aaa2, int bbb) : bbb2 = bbb;
}
''');
  }

  Future<void> test_multipleInitializers_second() async {
    await resolveTestCode('''
class A {
  int aaa2;
  int bbb2;
  A(int aaa, int ^bbb) : aaa2 = aaa, bbb2 = bbb;
}
''');
    await assertHasAssist('''
class A {
  int aaa2;
  int bbb2;
  A(int aaa, this.bbb2) : aaa2 = aaa;
}
''');
  }

  Future<void> test_multipleUses() async {
    await resolveTestCode('''
class A {
  int aaa;
  A(int ^aaa) : aaa = aaa {
    print(aaa + aaa + aaa);
  }
}
''');
    await assertHasAssist('''
class A {
  int aaa;
  A(this.aaa) {
    print(aaa + aaa + aaa);
  }
}
''');
  }

  Future<void> test_namedParameter() async {
    await resolveTestCode('''
class A {
  int? aaa;
  A({int? aaa}) : aaa = ^aaa;
}
''');
    await assertHasAssist('''
class A {
  int? aaa;
  A({this.aaa});
}
''');
  }

  Future<void> test_optionalParameter() async {
    await resolveTestCode('''
class A {
  int? aaa;
  A([int? aaa]) : aaa = ^aaa;
}
''');
    await assertHasAssist('''
class A {
  int? aaa;
  A([this.aaa]);
}
''');
  }

  Future<void> test_requiredNamed() async {
    await resolveTestCode('''
class C {
  final int foo;
  C({required int ^foo}) : foo = foo;
}
''');
    await assertHasAssist('''
class C {
  final int foo;
  C({required this.foo});
}
''');
  }

  Future<void> test_superParameter() async {
    await resolveTestCode(r'''
class B {
  B({int? i});
}

class C extends B {
  final int? _i;
  C({super.^i}) : _i = i;
}
''');
    await assertNoAssist();
  }
}

/// Tests how privacy interacts with the assist.
@reflectiveTest
class ConvertToInitializingFormalPrivateTest extends AssistProcessorTest {
  @override
  AssistKind get kind => DartAssistKind.convertToInitializingFormal;

  Future<void> test_named_differentTypes() async {
    await resolveTestCode('''
class C {
  num? _a;
  C({int? ^a = 1}) {
    this._a = a;
  }
}
''');
    await assertHasAssist('''
class C {
  num? _a;
  C({int? this._a = 1});
}
''');
  }

  Future<void> test_named_inAssignment() async {
    await resolveTestCode('''
class C {
  int? _a;
  C({int? ^a = 1}) {
    this._a = a;
  }
}
''');
    await assertHasAssist('''
class C {
  int? _a;
  C({this._a = 1});
}
''');
  }

  Future<void> test_named_inInitializer() async {
    await resolveTestCode('''
class C {
  Object? _a;
  C({Object? ^a}) : _a = a;
}
''');
    await assertHasAssist('''
class C {
  Object? _a;
  C({this._a});
}
''');
  }

  Future<void> test_named_inInitializer_referencedInBody_secondary() async {
    await resolveTestCode('''
class C {
  final int _i;

  C({required int ^i}) : _i = i {
    print(i);
  }
}
''');
    await assertHasAssist('''
class C {
  final int _i;

  C({required this._i}) {
    print(_i);
  }
}
''');
  }

  Future<void>
  test_named_inInitializer_referencedInFieldInitializer_primary() async {
    await resolveTestCode('''
class C({required int ^i}) {
  final int _i;
  final int _j = i;

  this : _i = i;
}
''');
    await assertHasAssist('''
class C({required this._i}) {
  final int _i;
  final int _j = _i;

  this;
}
''');
  }

  Future<void>
  test_named_inInitializer_referencedInInitializer_primary() async {
    await resolveTestCode('''
class C({required int ^i}) {
  final int _i;
  final int _j;

  this : _i = i, _j = i;
}
''');
    await assertHasAssist('''
class C({required this._i}) {
  final int _i;
  final int _j;

  this : _j = _i;
}
''');
  }

  Future<void>
  test_named_inInitializer_referencedInInitializer_secondary() async {
    await resolveTestCode('''
class C {
  final int _i;
  final int _j;

  C({required int ^i}) : _i = i, _j = i;
}
''');
    await assertHasAssist('''
class C {
  final int _i;
  final int _j;

  C({required this._i}) : _j = _i;
}
''');
  }

  Future<void> test_named_inInitializer_rightSide() async {
    await resolveTestCode('''
class C({required int i}) {
  int _i;
  this : _i = ^i;
}
''');
    await assertHasAssist('''
class C({required this._i}) {
  int _i;
  this;
}
''');
  }

  Future<void> test_named_unsupported() async {
    await resolveTestCode('''
// @dart=3.10
class C {
  int? _a;
  C({int? ^a = 1}) {
    this._a = a;
  }
}
''');
    await assertNoAssist();
  }
}

/// Tests when the assist can and can't apply if the field and parameter names
/// aren't the same.
@reflectiveTest
class ConvertToInitializingFormalRenameTest extends AssistProcessorTest {
  @override
  AssistKind get kind => DartAssistKind.convertToInitializingFormal;

  Future<void> test_namedParameter_assignment() async {
    await resolveTestCode('''
class A {
  int? field;
  A({int? ^param}) {
    this.field = param;
  }
}
''');
    await assertNoAssist();
  }

  Future<void> test_namedParameter_initializer() async {
    await resolveTestCode('''
class A {
  int? field;
  A({int? ^param}) : field = param;
}
''');
    await assertNoAssist();
  }

  Future<void> test_optionalParameter_assignment() async {
    await resolveTestCode('''
class A {
  int? field;
  A([int? ^param]) {
    this.field = param;
  }
}
''');
    await assertHasAssist('''
class A {
  int? field;
  A([this.field]);
}
''');
  }

  Future<void> test_optionalParameter_initializer() async {
    await resolveTestCode('''
class A {
  int? field;
  A([int? ^param]) : field = param;
}
''');
    await assertHasAssist('''
class A {
  int? field;
  A([this.field]);
}
''');
  }

  Future<void> test_positionalParameter_assignment() async {
    await resolveTestCode('''
class A {
  int? field;
  A(int? ^param) {
    this.field = param;
  }
}
''');
    await assertHasAssist('''
class A {
  int? field;
  A(this.field);
}
''');
  }

  Future<void> test_positionalParameter_initializer() async {
    await resolveTestCode('''
class A {
  int? field;
  A(int? ^param) : field = param;
}
''');
    await assertHasAssist('''
class A {
  int? field;
  A(this.field);
}
''');
  }
}
