/// Core validation utilities for form fields
library validators;

/// Validator function type
typedef FieldValidator = String? Function(String?)?;

/// Compose multiple validators
FieldValidator composeValidators(List<FieldValidator> validators) {
  return (String? value) {
    for (final validator in validators) {
      final result = validator?.call(value);
      if (result != null) {
        return result;
      }
    }
    return null;
  };
}

/// Common validators
class Validators {
  Validators._(); // Prevent instantiation

  /// Required field validator
  static FieldValidator required({String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return message ?? 'Ce champ est requis';
      }
      return null;
    };
  }

  /// Minimum length validator
  static FieldValidator minLength(int length, {String? message}) {
    return (String? value) {
      if (value != null && value.trim().length < length) {
        return message ?? 'Minimum $length caractères requis';
      }
      return null;
    };
  }

  /// Maximum length validator
  static FieldValidator maxLength(int length, {String? message}) {
    return (String? value) {
      if (value != null && value.length > length) {
        return message ?? 'Maximum $length caractères autorisés';
      }
      return null;
    };
  }

  /// Email validator
  static FieldValidator email({String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null; // Use required() separately if needed
      }

      final emailRegex = RegExp(
        r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
      );

      if (!emailRegex.hasMatch(value.trim())) {
        return message ?? 'Adresse email invalide';
      }
      return null;
    };
  }

  /// Numeric validator
  static FieldValidator numeric({String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null; // Use required() separately if needed
      }

      if (double.tryParse(value.trim()) == null) {
        return message ?? 'Veuillez entrer un nombre valide';
      }
      return null;
    };
  }

  /// Minimum value validator (for numeric fields)
  static FieldValidator minValue(double min, {String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null;
      }

      final numValue = double.tryParse(value.trim());
      if (numValue == null) {
        return 'Nombre invalide';
      }

      if (numValue < min) {
        return message ?? 'La valeur doit être au moins $min';
      }
      return null;
    };
  }

  /// Maximum value validator (for numeric fields)
  static FieldValidator maxValue(double max, {String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null;
      }

      final numValue = double.tryParse(value.trim());
      if (numValue == null) {
        return 'Nombre invalide';
      }

      if (numValue > max) {
        return message ?? 'La valeur doit être au maximum $max';
      }
      return null;
    };
  }

  /// Range validator (for numeric fields)
  static FieldValidator range(double min, double max, {String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null;
      }

      final numValue = double.tryParse(value.trim());
      if (numValue == null) {
        return 'Nombre invalide';
      }

      if (numValue < min || numValue > max) {
        return message ?? 'La valeur doit être entre $min et $max';
      }
      return null;
    };
  }

  /// Phone number validator (simple version)
  static FieldValidator phone({String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null;
      }

      // Allow digits, spaces, +, -, ()
      final phoneRegex = RegExp(r'^[\d\s\+\-\(\)]+$');
      if (!phoneRegex.hasMatch(value.trim())) {
        return message ?? 'Numéro de téléphone invalide';
      }

      // Check minimum length (at least 8 digits)
      final digitsOnly = value.replaceAll(RegExp(r'[^\d]'), '');
      if (digitsOnly.length < 8) {
        return message ?? 'Numéro de téléphone trop court';
      }

      return null;
    };
  }

  /// URL validator
  static FieldValidator url({String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null;
      }

      try {
        final uri = Uri.parse(value.trim());
        if (!uri.hasScheme || !uri.hasAuthority) {
          return message ?? 'URL invalide';
        }
      } catch (e) {
        return message ?? 'URL invalide';
      }

      return null;
    };
  }

  /// Pattern/Regex validator
  static FieldValidator pattern(RegExp regex, {String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null;
      }

      if (!regex.hasMatch(value.trim())) {
        return message ?? 'Format invalide';
      }
      return null;
    };
  }

  /// Match validator (confirm password, etc.)
  static FieldValidator match(String otherValue, {String? message}) {
    return (String? value) {
      if (value != otherValue) {
        return message ?? 'Les valeurs ne correspondent pas';
      }
      return null;
    };
  }

  /// Custom validator
  static FieldValidator custom(bool Function(String?) test, {String? message}) {
    return (String? value) {
      if (!test(value)) {
        return message ?? 'Valeur invalide';
      }
      return null;
    };
  }

  /// Alphanumeric validator
  static FieldValidator alphanumeric({String? message}) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null;
      }

      final alphanumericRegex = RegExp(r'^[a-zA-Z0-9]+$');
      if (!alphanumericRegex.hasMatch(value.trim())) {
        return message ?? 'Seuls les caractères alphanumériques sont autorisés';
      }
      return null;
    };
  }

  /// No whitespace validator
  static FieldValidator noWhitespace({String? message}) {
    return (String? value) {
      if (value == null) return null;

      if (value.contains(' ')) {
        return message ?? 'Les espaces ne sont pas autorisés';
      }
      return null;
    };
  }
}

/// Finance-specific validators
class FinanceValidators {
  FinanceValidators._();

  /// Amount validator (positive number with max 2 decimals)
  static FieldValidator amount({
    double? min,
    double? max,
    String? message,
  }) {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return null;
      }

      // Check if numeric
      final numValue = double.tryParse(value.trim());
      if (numValue == null) {
        return 'Montant invalide';
      }

      // Check if positive
      if (numValue <= 0) {
        return 'Le montant doit être positif';
      }

      // Check min/max
      if (min != null && numValue < min) {
        return message ?? 'Le montant minimum est $min';
      }
      if (max != null && numValue > max) {
        return message ?? 'Le montant maximum est $max';
      }

      // Check max 2 decimals
      final parts = value.trim().split('.');
      if (parts.length > 1 && parts[1].length > 2) {
        return 'Maximum 2 décimales autorisées';
      }

      return null;
    };
  }

  /// Budget line name validator
  static FieldValidator budgetLineName() {
    return composeValidators([
      Validators.required(message: 'Le nom de la ligne budgétaire est requis'),
      Validators.minLength(3, message: 'Minimum 3 caractères'),
      Validators.maxLength(100, message: 'Maximum 100 caractères'),
    ]);
  }

  /// Budget description validator
  static FieldValidator budgetDescription({bool required = false}) {
    return composeValidators([
      if (required)
        Validators.required(message: 'La description est requise'),
      Validators.maxLength(500, message: 'Maximum 500 caractères'),
    ]);
  }

  /// Rejection reason validator
  static FieldValidator rejectionReason() {
    return composeValidators([
      Validators.required(message: 'La raison du rejet est requise'),
      Validators.minLength(10, message: 'Veuillez fournir une raison plus détaillée (min 10 caractères)'),
      Validators.maxLength(500, message: 'Maximum 500 caractères'),
    ]);
  }

  /// Approval comment validator (optional but with constraints if provided)
  static FieldValidator approvalComment() {
    return composeValidators([
      Validators.maxLength(500, message: 'Maximum 500 caractères'),
    ]);
  }

  /// Budget name validator
  static FieldValidator budgetName() {
    return composeValidators([
      Validators.required(message: 'Le nom du budget est requis'),
      Validators.minLength(3, message: 'Minimum 3 caractères'),
      Validators.maxLength(200, message: 'Maximum 200 caractères'),
    ]);
  }

  /// Fiscal year validator
  static FieldValidator fiscalYear() {
    return (String? value) {
      if (value == null || value.trim().isEmpty) {
        return 'L\'année est requise';
      }

      final year = int.tryParse(value.trim());
      if (year == null) {
        return 'Année invalide';
      }

      final currentYear = DateTime.now().year;
      if (year < currentYear - 5 || year > currentYear + 10) {
        return 'L\'année doit être entre ${currentYear - 5} et ${currentYear + 10}';
      }

      return null;
    };
  }
}
