// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import 'dart:async';

import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';

class ComponentProxyGenerator
    extends GeneratorForAnnotation<ComponentProxyExtension> {
  @override
  FutureOr<String> generateForAnnotatedElement(
    Element element,
    ConstantReader annotation,
    BuildStep buildStep,
  ) {
    /// Check element type
    if (element is! ClassElement) {
      throw InvalidGenerationSourceError(
        'Only classes can be annotated with "CopyWith". "$element" is '
        'not a ClassElement.',
        element: element,
      );
    }

    final classAnnotation = ComponentProxyExtension(
      skipFields: annotation.peek('skipFields')?.boolValue ?? true,
    );

    final generatedCode = StringBuffer()
      ..write('abstract class \$${element.displayName}CWProxy {');

    if (classAnnotation.skipFields ?? true) {
      for (final field in element.constructors.first.parameters) {
        generatedCode.write(
          '${element.displayName} '
          '${field.displayName}(${field.type.getDisplayString(
            withNullability: false,
          )}? '
          '${field.displayName});',
        );
      }
    }

    generatedCode.write('${element.displayName} call({');

    for (final field in element.constructors.first.parameters) {
      generatedCode.write('${field.type.getDisplayString(
        withNullability: false,
      )}? ${field.displayName}, ');
    }

    generatedCode.write('});}');

    return generatedCode.toString();
  }
}
