// Copyright (C) 2022 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 'package:wyatt_form_bloc/src/core/enums/form_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';
import 'package:wyatt_form_bloc/src/data/form_validators/every_input_validator.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart';
import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart';
import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart';
import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart';
import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart';
import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart';

// ignore: must_be_immutable
class WyattFormImpl extends WyattForm {

  WyattFormImpl(
    this._inputs, {
    required String name,
    FormValidator validationStrategy = const EveryInputValidator(),
  })  : _name = name,
        _validator = validationStrategy {
    _inputsInitial = _inputs.map((input) => input.clone()).toList();
  }
  final List<
      FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
          dynamic>> _inputs;
  final FormValidator _validator;
  final String _name;

  late List<
      FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
          dynamic>> _inputsInitial;

  @override
  List<
      FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
          dynamic>> get inputs => _inputs;

  @override
  FormValidator get formValidationStrategy => _validator;

  @override
  String get name => _name;

  @override
  bool containsKey(String key) => inputs.any((input) => input.key == key);

  @override
  List<FormInputValidator<Value, Error>>
      asValidatorList<Value, Error extends ValidationError>() => inputs
          .map<FormInputValidator<Value, Error>>(
            (input) => input.validator as FormInputValidator<Value, Error>,
          )
          .toList();

  @override
  FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, dynamic>
      inputOf(String key) {
    if (containsKey(key)) {
      return inputs.firstWhere((input) => input.key == key);
    } else {
      throw Exception(
        'FormInput with key `$key` does not exist in form `$name`',
      );
    }
  }

  @override
  Error? errorOf<Error extends ValidationError>(String key) =>
      (inputOf(key).validator as FormInputValidator<dynamic, Error>).error;

  @override
  FormInputMetadata<Extra> metadataOf<Extra>(String key) =>
      inputOf(key).metadata as FormInputMetadata<Extra>;

  @override
  Validator validatorOf<
          Validator extends FormInputValidator<dynamic, ValidationError>>(
    String key,
  ) =>
      inputOf(key).validator as Validator;

  @override
  Value? valueOf<Value>(String key) =>
      (inputOf(key).validator as FormInputValidator<Value, dynamic>).value;

  @override
  FormStatus validate() => formValidationStrategy.validate(this);

  @override
  WyattForm clone() {
    final clone = WyattFormImpl(
      _inputs.map((input) => input.clone()).toList(),
      name: _name,
      validationStrategy: formValidationStrategy,
    ).._inputsInitial = _inputsInitial;
    return clone;
  }

  @override
  WyattForm reset() {
    final newForm = WyattFormImpl(
      _inputsInitial,
      name: _name,
      validationStrategy: formValidationStrategy,
    ).._inputsInitial = _inputsInitial;
    return newForm;
  }

  @override
  void updateMetadata<Extra>(String key, FormInputMetadata<Extra> metadata) {
    final index = _inputs.indexOf(
      inputOf(key),
    );
    _inputs[index] = _inputs[index].copyWith(metadata: metadata);
  }

  @override
  void updateValidator<Value>(
    String key,
    FormInputValidator<Value?, ValidationError> dirtyValue,
  ) {
    final index = _inputs.indexOf(
      inputOf(key),
    );
    _inputs[index] = _inputs[index].copyWith(validator: dirtyValue);
  }

  @override
  WyattForm operationWith(FormOperation operation, WyattForm other) =>
      operation.call(this, other);

  @override
  bool isPureInput(String key) => inputOf(key).validator.pure;

  @override
  bool isValidInput(String key) => inputOf(key).validator.valid;
  
  @override
  bool isInvalidInput(String key) => inputOf(key).validator.invalid;

  @override
  List<Object?> get props => [_inputs, _name, _validator];

  @override
  String toString() => 'WyattForm(name: $name, validation: '
      '${_validator.runtimeType}, inputs: $inputs)';
}
