import 'package:equatable/equatable.dart';

/// Classe de base abstraite pour toutes les erreurs de l'application.
///
/// Les [Failure] représentent des erreurs métier qui peuvent être gérées
/// de manière élégante par l'application, contrairement aux [Exception]
/// qui sont des erreurs techniques.
///
/// **Usage:**
/// ```dart
/// try {
///   final result = await repository.getData();
///   return Right(result);
/// } catch (e) {
///   return Left(ServerFailure(message: e.toString()));
/// }
/// ```
abstract class Failure extends Equatable {
  /// Crée une nouvelle [Failure].
  ///
  /// [message] Un message optionnel décrivant l'erreur
  /// [code] Un code d'erreur optionnel
  const Failure({
    this.message = 'Une erreur est survenue',
    this.code,
  });

  /// Message décrivant l'erreur
  final String message;

  /// Code d'erreur optionnel
  final String? code;

  @override
  List<Object?> get props => [message, code];

  @override
  String toString() => 'Failure(message: $message, code: $code)';
}

/// Erreur liée au serveur ou à la communication réseau.
///
/// Cette erreur est levée lorsque :
/// - Une requête HTTP échoue
/// - Le serveur retourne une erreur
/// - La connexion réseau est perdue
/// - Un timeout se produit
///
/// **Exemple:**
/// ```dart
/// try {
///   final response = await http.get(url);
///   if (response.statusCode != 200) {
///     throw ServerFailure(
///       message: 'Erreur serveur: ${response.statusCode}',
///       code: response.statusCode.toString(),
///     );
///   }
/// } catch (e) {
///   throw ServerFailure(message: e.toString());
/// }
/// ```
class ServerFailure extends Failure {
  /// Crée une nouvelle [ServerFailure].
  ///
  /// [message] Message décrivant l'erreur serveur
  /// [code] Code d'erreur HTTP optionnel
  /// [statusCode] Code de statut HTTP optionnel
  const ServerFailure({
    super.message = 'Erreur serveur',
    super.code,
    this.statusCode,
  });

  /// Code de statut HTTP (404, 500, etc.)
  final int? statusCode;

  @override
  List<Object?> get props => [...super.props, statusCode];

  @override
  String toString() =>
      'ServerFailure(message: $message, code: $code, statusCode: $statusCode)';
}

/// Erreur liée au cache local.
///
/// Cette erreur est levée lorsque :
/// - Les données ne peuvent pas être lues depuis le cache
/// - Les données ne peuvent pas être écrites dans le cache
/// - Le cache est corrompu ou inaccessible
///
/// **Exemple:**
/// ```dart
/// try {
///   final cachedData = await cache.get(key);
///   if (cachedData == null) {
///     throw CacheFailure(message: 'Données non trouvées dans le cache');
///   }
/// } catch (e) {
///   throw CacheFailure(message: e.toString());
/// }
/// ```
class CacheFailure extends Failure {
  /// Crée une nouvelle [CacheFailure].
  ///
  /// [message] Message décrivant l'erreur de cache
  /// [code] Code d'erreur optionnel
  const CacheFailure({
    super.message = 'Erreur de cache',
    super.code,
  });

  @override
  String toString() => 'CacheFailure(message: $message, code: $code)';
}

/// Erreur liée à l'authentification.
///
/// Cette erreur est levée lorsque :
/// - Les identifiants sont incorrects
/// - Le token d'authentification est expiré
/// - L'utilisateur n'est pas autorisé
///
/// **Exemple:**
/// ```dart
/// if (!isAuthenticated) {
///   throw AuthenticationFailure(
///     message: 'Authentification requise',
///   );
/// }
/// ```
class AuthenticationFailure extends Failure {
  /// Crée une nouvelle [AuthenticationFailure].
  ///
  /// [message] Message décrivant l'erreur d'authentification
  /// [code] Code d'erreur optionnel
  const AuthenticationFailure({
    super.message = 'Erreur d\'authentification',
    super.code,
  });

  @override
  String toString() =>
      'AuthenticationFailure(message: $message, code: $code)';
}

/// Erreur liée à la validation des données.
///
/// Cette erreur est levée lorsque :
/// - Les données saisies sont invalides
/// - Les données ne respectent pas les contraintes
/// - Les données sont manquantes
///
/// **Exemple:**
/// ```dart
/// if (email.isEmpty || !isValidEmail(email)) {
///   throw ValidationFailure(
///     message: 'Email invalide',
///     field: 'email',
///   );
/// }
/// ```
class ValidationFailure extends Failure {
  /// Crée une nouvelle [ValidationFailure].
  ///
  /// [message] Message décrivant l'erreur de validation
  /// [field] Le champ qui a échoué la validation
  /// [code] Code d'erreur optionnel
  const ValidationFailure({
    super.message = 'Erreur de validation',
    this.field,
    super.code,
  });

  /// Le champ qui a échoué la validation
  final String? field;

  @override
  List<Object?> get props => [super.props, field];

  @override
  String toString() =>
      'ValidationFailure(message: $message, field: $field, code: $code)';
}

/// Erreur liée à une opération réseau.
///
/// Cette erreur est levée lorsque :
/// - La connexion Internet est perdue
/// - Le timeout est dépassé
/// - La connexion est refusée
///
/// **Exemple:**
/// ```dart
/// try {
///   final response = await http.get(url).timeout(
///     const Duration(seconds: 5),
///   );
/// } on TimeoutException {
///   throw NetworkFailure(message: 'Timeout de connexion');
/// } on SocketException {
///   throw NetworkFailure(message: 'Pas de connexion Internet');
/// }
/// ```
class NetworkFailure extends Failure {
  /// Crée une nouvelle [NetworkFailure].
  ///
  /// [message] Message décrivant l'erreur réseau
  /// [code] Code d'erreur optionnel
  const NetworkFailure({
    super.message = 'Erreur réseau',
    super.code,
  });

  @override
  String toString() => 'NetworkFailure(message: $message, code: $code)';
}
