/// Implémentation du repository pour la gestion des événements
/// Interface avec l'API backend EvenementResource
library evenement_repository_impl;

import 'package:dio/dio.dart';
import 'package:unionflow_mobile_apps/core/network/api_client.dart';
import 'package:injectable/injectable.dart';
import '../../domain/repositories/evenement_repository.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,
      );
    }
  }
}

/// Implémentation du repository des événements
@LazySingleton(as: IEvenementRepository)
class EvenementRepositoryImpl implements IEvenementRepository {
  final ApiClient _apiClient;
  static const String _baseUrl = '/api/evenements';

  EvenementRepositoryImpl(this._apiClient);

  /// Parse une réponse API : liste directe ou objet paginé (content / data).
  EvenementSearchResult _parseSearchResponse(dynamic data, int page, int size) {
    if (data is List) {
      final evenements = (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,
      );
    }
    return EvenementSearchResult.fromJson(data as Map<String, dynamic>);
  }

  @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 _apiClient.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 {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<EvenementModel?> getEvenementById(String id) async {
    try {
      final response = await _apiClient.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.type == DioExceptionType.cancel) rethrow;
      if (e.response?.statusCode == 404) {
        return null;
      }
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<EvenementModel> createEvenement(EvenementModel evenement) async {
    try {
      final response = await _apiClient.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 {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<EvenementModel> updateEvenement(String id, EvenementModel evenement) async {
    try {
      final response = await _apiClient.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 {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<void> deleteEvenement(String id) async {
    try {
      final response = await _apiClient.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 {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

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

      if (response.statusCode == 200) {
        return _parseSearchResponse(response.data, page, size);
      } else {
        throw Exception('Erreur lors de la récupération des événements à venir: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

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

      if (response.statusCode == 200) {
        return _parseSearchResponse(response.data, page, size);
      } else {
        throw Exception('Erreur lors de la récupération des événements en cours: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

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

      if (response.statusCode == 200) {
        return _parseSearchResponse(response.data, page, size);
      } else {
        throw Exception('Erreur lors de la récupération des événements passés: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

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

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

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

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

  @override
  Future<List<Map<String, dynamic>>> getParticipants(String evenementId) async {
    try {
      final response = await _apiClient.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 {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<bool> getInscriptionStatus(String evenementId) async {
    try {
      final response = await _apiClient.get('$_baseUrl/$evenementId/me/inscrit');
      if (response.statusCode == 200 && response.data is Map) {
        final data = response.data as Map<String, dynamic>;
        return data['inscrit'] == true;
      }
      return false;
    } on DioException catch (e) {
      if (e.type == DioExceptionType.cancel) rethrow;
      return false;
    } catch (_) {
      return false;
    }
  }

  @override
  Future<Map<String, dynamic>> getEvenementsStats() async {
    try {
      final response = await _apiClient.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 {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<void> submitFeedback({
    required String evenementId,
    required int note,
    String? commentaire,
  }) async {
    try {
      final response = await _apiClient.post(
        '$_baseUrl/$evenementId/feedback',
        data: {
          'note': note,
          if (commentaire != null) 'commentaire': commentaire,
        },
      );

      if (response.statusCode != 200 && response.statusCode != 201) {
        throw Exception('Erreur lors de la soumission du feedback: ${response.statusCode}');
      }
    } on DioException catch (e) {
      if (e.type == DioExceptionType.cancel) rethrow;
      if (e.response?.statusCode == 400) {
        // Erreur métier (déjà soumis, pas participant, etc.)
        final errorMsg = e.response?.data['error'] ?? 'Erreur lors de la soumission du feedback';
        throw Exception(errorMsg);
      }
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<Map<String, dynamic>> getFeedbacks(String evenementId) async {
    try {
      final response = await _apiClient.get('$_baseUrl/$evenementId/feedbacks');

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

