// 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:equatable/equatable.dart';
import 'package:wyatt_form_bloc/src/core/enums/form_input_status.dart';
import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart';

part 'any_validator.dart';
part 'equality_validator.dart';
part 'nullable_validator.dart';
part 'regex_validator.dart';
part 'text_validator.dart';

/// {@template form_input_validator}
/// A [FormInputValidator] represents the value of a single form input field.
/// It contains information about the [FormInputStatus], [value], as well
/// as validation status.
///
/// [FormInputValidator] should be extended to define custom
/// [FormInputValidator] instances.
///
/// ```dart
/// class Name extends FormInputValidator<String, ValidationStandardError> {
///   const Name.pure({String value = ''}) : super.pure(value);
///   const Name.dirty({String value = ''}) : super.dirty(value);
///
///   @override
///   ValidationStandardError? validator(String? value) {
///     if (value?.isEmpty ?? false) {
///       return ValidationStandardError.empty
///     }
///     return value == null ? ValidationStandardError.invalid : null;
///   }
/// }
/// ```
/// {@endtemplate}
abstract class FormInputValidator<Value, Error extends ValidationError>
    extends Equatable {
  const FormInputValidator._(this.value, [this.pure = true]);

  /// Constructor which create a `pure` [FormInputValidator] with a given value.
  const FormInputValidator.pure(Value value) : this._(value);

  /// Constructor which create a `dirty` [FormInputValidator] with a
  /// given value.
  const FormInputValidator.dirty(Value value) : this._(value, false);

  /// The value of the given [FormInputValidator].
  /// For example, if you have a `FormInputValidator` for `FirstName`,
  /// the value could be 'Joe'.
  final Value? value;

  /// If the [FormInputValidator] is pure (has been touched/modified).
  /// Typically when the `FormInputValidator` is initially created,
  /// it is created using the `FormInputValidator.pure` constructor to
  /// signify that the user has not modified it.
  ///
  /// For subsequent changes (in response to user input), the
  /// `FormInputValidator.dirty` constructor should be used to signify that
  /// the `FormInputValidator` has been manipulated.
  final bool pure;

  /// The [FormInputStatus] which can be one of the following:
  /// * [FormInputStatus.pure]
  ///   - if the input has not been modified.
  /// * [FormInputStatus.invalid]
  ///   - if the input has been modified and validation failed.
  /// * [FormInputStatus.valid]
  ///   - if the input has been modified and validation succeeded.
  FormInputStatus get status => pure
      ? FormInputStatus.pure
      : valid
          ? FormInputStatus.valid
          : FormInputStatus.invalid;

  /// Returns a validation error if the [FormInputValidator] is invalid.
  /// Returns `null` if the [FormInputValidator] is valid.
  Error? get error => validator(value);

  /// Whether the [FormInputValidator] value is valid according to the
  /// overridden `validator`.
  ///
  /// Returns `true` if `validator` returns `null` for the
  /// current [FormInputValidator] value and `false` otherwise.
  bool get valid => validator(value) == null;

  /// Whether the [FormInputValidator] value is not valid.
  /// A value is invalid when the overridden `validator`
  /// returns an error (non-null value).
  bool get invalid => status == FormInputStatus.invalid;

  /// A function that must return a validation error if the provided
  /// [value] is invalid and `null` otherwise.
  Error? validator(Value? value);

  @override
  List<Object?> get props => [value, pure];

  @override
  String toString() => 'value: $value, status: ${status.name}';
}
