/// Repository pour la gestion des événements
/// Interface avec l'API backend EvenementResource
library evenement_repository;

import 'package:dio/dio.dart';
import '../models/evenement_model.dart';

/// Résultat de recherche paginé
class EvenementSearchResult {
  final List<EvenementModel> evenements;
  final int total;
  final int page;
  final int size;
  final int totalPages;

  const EvenementSearchResult({
    required this.evenements,
    required this.total,
    required this.page,
    required this.size,
    required this.totalPages,
  });

  factory EvenementSearchResult.fromJson(Map<String, dynamic> json) {
    // Support pour les deux formats de réponse
    if (json.containsKey('data')) {
      // Format paginé avec métadonnées
      return EvenementSearchResult(
        evenements: (json['data'] as List<dynamic>)
            .map((e) => EvenementModel.fromJson(e as Map<String, dynamic>))
            .toList(),
        total: json['total'] as int,
        page: json['page'] as int,
        size: json['size'] as int,
        totalPages: json['totalPages'] as int,
      );
    } else {
      // Format simple (liste directe) - pour compatibilité backend
      return EvenementSearchResult(
        evenements: (json['content'] as List<dynamic>)
            .map((e) => EvenementModel.fromJson(e as Map<String, dynamic>))
            .toList(),
        total: json['totalElements'] as int? ?? 0,
        page: json['number'] as int? ?? 0,
        size: json['size'] as int? ?? 20,
        totalPages: json['totalPages'] as int? ?? 1,
      );
    }
  }
}

/// Interface du repository des événements
abstract class EvenementRepository {
  /// Récupère la liste des événements avec pagination
  Future<EvenementSearchResult> getEvenements({
    int page = 0,
    int size = 20,
    String? recherche,
  });

  /// Récupère un événement par son ID
  Future<EvenementModel?> getEvenementById(String id);

  /// Crée un nouvel événement
  Future<EvenementModel> createEvenement(EvenementModel evenement);

  /// Met à jour un événement
  Future<EvenementModel> updateEvenement(String id, EvenementModel evenement);

  /// Supprime un événement
  Future<void> deleteEvenement(String id);

  /// Récupère les événements à venir
  Future<EvenementSearchResult> getEvenementsAVenir({int page = 0, int size = 20});

  /// Récupère les événements en cours
  Future<EvenementSearchResult> getEvenementsEnCours({int page = 0, int size = 20});

  /// Récupère les événements passés
  Future<EvenementSearchResult> getEvenementsPasses({int page = 0, int size = 20});

  /// S'inscrire à un événement
  Future<void> inscrireEvenement(String evenementId);

  /// Se désinscrire d'un événement
  Future<void> desinscrireEvenement(String evenementId);

  /// Récupère les participants d'un événement
  Future<List<Map<String, dynamic>>> getParticipants(String evenementId);

  /// Récupère les statistiques des événements
  Future<Map<String, dynamic>> getEvenementsStats();
}

/// Implémentation du repository des événements
class EvenementRepositoryImpl implements EvenementRepository {
  final Dio _dio;
  static const String _baseUrl = '/api/evenements';

  EvenementRepositoryImpl(this._dio);

  @override
  Future<EvenementSearchResult> getEvenements({
    int page = 0,
    int size = 20,
    String? recherche,
  }) async {
    try {
      final queryParams = <String, dynamic>{
        'page': page,
        'size': size,
      };

      if (recherche?.isNotEmpty == true) {
        queryParams['recherche'] = recherche;
      }

      final response = await _dio.get(
        _baseUrl,
        queryParameters: queryParams,
      );

      if (response.statusCode == 200) {
        // Le backend peut retourner soit une liste directe, soit un objet paginé
        if (response.data is List) {
          // Format liste directe
          final evenements = (response.data as List<dynamic>)
              .map((e) => EvenementModel.fromJson(e as Map<String, dynamic>))
              .toList();
          return EvenementSearchResult(
            evenements: evenements,
            total: evenements.length,
            page: page,
            size: size,
            totalPages: 1,
          );
        } else {
          // Format objet paginé
          return EvenementSearchResult.fromJson(response.data as Map<String, dynamic>);
        }
      } else {
        throw Exception('Erreur lors de la récupération des événements: ${response.statusCode}');
      }
    } on DioException catch (e) {
      if (e.response != null) {
        throw Exception('Erreur HTTP ${e.response!.statusCode}: ${e.response!.data}');
      } else {
        throw Exception('Erreur réseau: ${e.type} - ${e.message ?? e.error}');
      }
    } catch (e) {
      throw Exception('Erreur inattendue lors de la récupération des événements: $e');
    }
  }

  @override
  Future<EvenementModel?> getEvenementById(String id) async {
    try {
      final response = await _dio.get('$_baseUrl/$id');

      if (response.statusCode == 200) {
        return EvenementModel.fromJson(response.data as Map<String, dynamic>);
      } else if (response.statusCode == 404) {
        return null;
      } else {
        throw Exception('Erreur lors de la récupération de l\'événement: ${response.statusCode}');
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        return null;
      }
      throw Exception('Erreur réseau lors de la récupération de l\'événement: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la récupération de l\'événement: $e');
    }
  }

  @override
  Future<EvenementModel> createEvenement(EvenementModel evenement) async {
    try {
      final response = await _dio.post(
        _baseUrl,
        data: evenement.toJson(),
      );

      if (response.statusCode == 201 || response.statusCode == 200) {
        return EvenementModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la création de l\'événement: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la création de l\'événement: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la création de l\'événement: $e');
    }
  }

  @override
  Future<EvenementModel> updateEvenement(String id, EvenementModel evenement) async {
    try {
      final response = await _dio.put(
        '$_baseUrl/$id',
        data: evenement.toJson(),
      );

      if (response.statusCode == 200) {
        return EvenementModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la mise à jour de l\'événement: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la mise à jour de l\'événement: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la mise à jour de l\'événement: $e');
    }
  }

  @override
  Future<void> deleteEvenement(String id) async {
    try {
      final response = await _dio.delete('$_baseUrl/$id');

      if (response.statusCode != 204 && response.statusCode != 200) {
        throw Exception('Erreur lors de la suppression de l\'événement: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la suppression de l\'événement: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la suppression de l\'événement: $e');
    }
  }

  @override
  Future<EvenementSearchResult> getEvenementsAVenir({int page = 0, int size = 20}) async {
    try {
      final response = await _dio.get(
        '$_baseUrl/a-venir',
        queryParameters: {'page': page, 'size': size},
      );

      if (response.statusCode == 200) {
        return EvenementSearchResult.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la récupération des événements à venir: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la récupération des événements à venir: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la récupération des événements à venir: $e');
    }
  }

  @override
  Future<EvenementSearchResult> getEvenementsEnCours({int page = 0, int size = 20}) async {
    try {
      final response = await _dio.get(
        '$_baseUrl/en-cours',
        queryParameters: {'page': page, 'size': size},
      );

      if (response.statusCode == 200) {
        return EvenementSearchResult.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la récupération des événements en cours: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la récupération des événements en cours: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la récupération des événements en cours: $e');
    }
  }

  @override
  Future<EvenementSearchResult> getEvenementsPasses({int page = 0, int size = 20}) async {
    try {
      final response = await _dio.get(
        '$_baseUrl/passes',
        queryParameters: {'page': page, 'size': size},
      );

      if (response.statusCode == 200) {
        return EvenementSearchResult.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la récupération des événements passés: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la récupération des événements passés: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la récupération des événements passés: $e');
    }
  }

  @override
  Future<void> inscrireEvenement(String evenementId) async {
    try {
      final response = await _dio.post('$_baseUrl/$evenementId/inscrire');

      if (response.statusCode != 200 && response.statusCode != 201) {
        throw Exception('Erreur lors de l\'inscription à l\'événement: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de l\'inscription à l\'événement: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de l\'inscription à l\'événement: $e');
    }
  }

  @override
  Future<void> desinscrireEvenement(String evenementId) async {
    try {
      final response = await _dio.delete('$_baseUrl/$evenementId/desinscrire');

      if (response.statusCode != 200 && response.statusCode != 204) {
        throw Exception('Erreur lors de la désinscription de l\'événement: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la désinscription de l\'événement: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la désinscription de l\'événement: $e');
    }
  }

  @override
  Future<List<Map<String, dynamic>>> getParticipants(String evenementId) async {
    try {
      final response = await _dio.get('$_baseUrl/$evenementId/participants');

      if (response.statusCode == 200) {
        return (response.data as List<dynamic>)
            .map((e) => e as Map<String, dynamic>)
            .toList();
      } else {
        throw Exception('Erreur lors de la récupération des participants: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la récupération des participants: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la récupération des participants: $e');
    }
  }

  @override
  Future<Map<String, dynamic>> getEvenementsStats() async {
    try {
      final response = await _dio.get('$_baseUrl/statistiques');

      if (response.statusCode == 200) {
        return response.data as Map<String, dynamic>;
      } else {
        throw Exception('Erreur lors de la récupération des statistiques: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la récupération des statistiques: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la récupération des statistiques: $e');
    }
  }
}

