import 'package:dartz/dartz.dart';
import '../errors/failures.dart';

/// Classe utilitaire pour convertir et valider les entrées utilisateur.
///
/// Cette classe fournit des méthodes pour convertir des chaînes en types
/// numériques et valider les entrées utilisateur de manière fonctionnelle.
///
/// **Usage:**
/// ```dart
/// final converter = InputConverter();
/// final result = converter.stringToUnsignedInteger('123');
/// result.fold(
///   (failure) => print('Erreur: $failure'),
///   (value) => print('Valeur: $value'),
/// );
/// ```
class InputConverter {
  /// Convertit une chaîne en entier non signé.
  ///
  /// [str] La chaîne à convertir
  ///
  /// Returns [Right] avec l'entier si la conversion réussit,
  /// [Left] avec [InvalidInputFailure] si la conversion échoue.
  ///
  /// **Exemple:**
  /// ```dart
  /// final result = converter.stringToUnsignedInteger('123');
  /// // Right(123)
  ///
  /// final result2 = converter.stringToUnsignedInteger('-5');
  /// // Left(InvalidInputFailure)
  /// ```
  Either<Failure, int> stringToUnsignedInteger(String str) {
    try {
      final trimmed = str.trim();
      if (trimmed.isEmpty) {
        return Left(const InvalidInputFailure(message: 'La chaîne est vide'));
      }

      final integer = int.parse(trimmed);
      if (integer < 0) {
        return Left(const InvalidInputFailure(message: 'Le nombre doit être positif'));
      }

      return Right(integer);
    } on FormatException {
      return Left(InvalidInputFailure(message: 'Format invalide: "$str"'));
    } catch (e) {
      return Left(InvalidInputFailure(message: 'Erreur de conversion: $e'));
    }
  }

  /// Convertit une chaîne en entier signé.
  ///
  /// [str] La chaîne à convertir
  ///
  /// Returns [Right] avec l'entier si la conversion réussit,
  /// [Left] avec [InvalidInputFailure] si la conversion échoue.
  Either<Failure, int> stringToInteger(String str) {
    try {
      final trimmed = str.trim();
      if (trimmed.isEmpty) {
        return Left(const InvalidInputFailure(message: 'La chaîne est vide'));
      }

      final integer = int.parse(trimmed);
      return Right(integer);
    } on FormatException {
      return Left(InvalidInputFailure(message: 'Format invalide: "$str"'));
    } catch (e) {
      return Left(InvalidInputFailure(message: 'Erreur de conversion: $e'));
    }
  }

  /// Convertit une chaîne en nombre décimal (double).
  ///
  /// [str] La chaîne à convertir
  ///
  /// Returns [Right] avec le double si la conversion réussit,
  /// [Left] avec [InvalidInputFailure] si la conversion échoue.
  Either<Failure, double> stringToDouble(String str) {
    try {
      final trimmed = str.trim();
      if (trimmed.isEmpty) {
        return Left(const InvalidInputFailure(message: 'La chaîne est vide'));
      }

      final doubleValue = double.parse(trimmed);
      return Right(doubleValue);
    } on FormatException {
      return Left(InvalidInputFailure(message: 'Format invalide: "$str"'));
    } catch (e) {
      return Left(InvalidInputFailure(message: 'Erreur de conversion: $e'));
    }
  }

  /// Convertit une chaîne en nombre décimal non négatif.
  ///
  /// [str] La chaîne à convertir
  ///
  /// Returns [Right] avec le double si la conversion réussit,
  /// [Left] avec [InvalidInputFailure] si la conversion échoue ou si négatif.
  Either<Failure, double> stringToUnsignedDouble(String str) {
    try {
      final trimmed = str.trim();
      if (trimmed.isEmpty) {
        return Left(const InvalidInputFailure(message: 'La chaîne est vide'));
      }

      final doubleValue = double.parse(trimmed);
      if (doubleValue < 0) {
        return Left(const InvalidInputFailure(message: 'Le nombre doit être positif'));
      }

      return Right(doubleValue);
    } on FormatException {
      return Left(InvalidInputFailure(message: 'Format invalide: "$str"'));
    } catch (e) {
      return Left(InvalidInputFailure(message: 'Erreur de conversion: $e'));
    }
  }

  /// Valide qu'une chaîne n'est pas vide.
  ///
  /// [str] La chaîne à valider
  ///
  /// Returns [Right] avec la chaîne si valide,
  /// [Left] avec [InvalidInputFailure] si vide.
  Either<Failure, String> validateNonEmpty(String str) {
    final trimmed = str.trim();
    if (trimmed.isEmpty) {
      return Left(const InvalidInputFailure(message: 'La chaîne ne peut pas être vide'));
    }
    return Right(trimmed);
  }
}

/// Erreur levée lorsque l'entrée utilisateur est invalide.
///
/// Cette classe représente une erreur de validation ou de conversion
/// des entrées utilisateur.
class InvalidInputFailure extends Failure {
  /// Crée une nouvelle [InvalidInputFailure].
  ///
  /// [message] Message décrivant l'erreur
  const InvalidInputFailure({
    super.message = 'Entrée invalide',
    super.code,
  });

  @override
  String toString() => 'InvalidInputFailure: $message';
}
