/// Implémentation du repository pour la gestion des membres
/// Interface avec l'API backend MembreResource
library membre_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/membre_repository.dart';
import '../models/membre_complete_model.dart';
import '../../../../shared/models/membre_search_result.dart';
import '../../../../shared/models/membre_search_criteria.dart';

/// Implémentation du repository des membres
@LazySingleton(as: IMembreRepository)
class MembreRepositoryImpl implements IMembreRepository {
  final ApiClient _apiClient;
  static const String _baseUrl = '/api/membres';

  MembreRepositoryImpl(this._apiClient);

  @override
  Future<MembreSearchResult> getMembres({
    int page = 0,
    int size = 20,
    String? recherche,
  }) async {
    try {
      // Si une recherche est fournie, utiliser l'endpoint de recherche
      if (recherche?.isNotEmpty == true) {
        final response = await _apiClient.get(
          '$_baseUrl/recherche',
          queryParameters: {
            'q': recherche,
            'page': page,
            'size': size,
          },
        );

        return _parseMembreSearchResult(response, page, size, MembreSearchCriteria(query: recherche));
      }

      // Sinon, récupérer tous les membres
      final response = await _apiClient.get(
        _baseUrl,
        queryParameters: {
          'page': page,
          'size': size,
        },
      );

      return _parseMembreSearchResult(response, page, size, const MembreSearchCriteria());
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  /// Normalise les clés backend pour alignement API ↔ modèle (cohérence des données).
  MembreCompletModel _normalizeAndParseMembre(Map<String, dynamic> map) {
    if (map.containsKey('associationNom') && !map.containsKey('organisationNom')) {
      map['organisationNom'] = map['associationNom'];
    }
    if (map.containsKey('organisationId') && map['organisationId'] != null && map['organisationId'] is! String) {
      map['organisationId'] = map['organisationId'].toString();
    }
    // Mapping statutCompte → statut avec normalisation des valeurs backend
    if (map.containsKey('statutCompte') && !map.containsKey('statut')) {
      final sc = (map['statutCompte'] as String? ?? '').toUpperCase();
      if (sc == 'ACTIF') {
        map['statut'] = 'ACTIF';
      } else if (sc == 'INACTIF') {
        map['statut'] = 'INACTIF';
      } else if (sc == 'SUSPENDU') {
        map['statut'] = 'SUSPENDU';
      } else {
        map['statut'] = 'EN_ATTENTE';
      }
    }
    if (map.containsKey('photoUrl') && !map.containsKey('photo')) {
      map['photo'] = map['photoUrl'];
    }
    // roles (List) → role (premier rôle)
    if (map.containsKey('roles') && !map.containsKey('role')) {
      final roles = map['roles'];
      if (roles is List && roles.isNotEmpty) {
        map['role'] = roles.first?.toString();
      }
    }
    if (map['id'] != null && map['id'] is! String) {
      map['id'] = map['id'].toString();
    }
    return MembreCompletModel.fromJson(map);
  }

  /// Parse la réponse API et retourne un MembreSearchResult
  /// Gère les deux formats possibles : List (simple) ou Map (paginé)
  MembreSearchResult _parseMembreSearchResult(
    Response response,
    int page,
    int size,
    MembreSearchCriteria criteria,
  ) {
    if (response.statusCode != 200) {
      throw Exception('Erreur HTTP: ${response.statusCode}');
    }

    // Format simple : liste directe de membres
    if (response.data is List) {
      final List<dynamic> listData = response.data as List<dynamic>;
      final membres = listData
          .map((e) => _normalizeAndParseMembre(e as Map<String, dynamic>))
          .toList();

      return MembreSearchResult(
        membres: membres,
        totalElements: membres.length,
        totalPages: 1,
        currentPage: page,
        pageSize: membres.length,
        numberOfElements: membres.length,
        hasNext: false,
        hasPrevious: false,
        isFirst: true,
        isLast: true,
        criteria: criteria,
        executionTimeMs: 0,
      );
    }

    // Format paginé : PagedResponse backend (data, total, page, size, totalPages)
    final Map<String, dynamic> data = response.data as Map<String, dynamic>;
    final List<dynamic>? listData = data['data'] as List<dynamic>?;
    if (listData != null) {
      final membres = listData
          .map((e) => _normalizeAndParseMembre(Map<String, dynamic>.from(e as Map<String, dynamic>)))
          .toList();
      final total = (data['total'] as num?)?.toInt() ?? membres.length;
      final currentPage = (data['page'] as num?)?.toInt() ?? page;
      final pageSize = (data['size'] as num?)?.toInt() ?? size;
      final totalPages = (data['totalPages'] as num?)?.toInt() ?? (total > 0 ? 1 : 0);
      return MembreSearchResult(
        membres: membres,
        totalElements: total,
        totalPages: totalPages,
        currentPage: currentPage,
        pageSize: pageSize,
        numberOfElements: membres.length,
        hasNext: currentPage + 1 < totalPages,
        hasPrevious: currentPage > 0,
        isFirst: currentPage == 0,
        isLast: currentPage >= totalPages - 1,
        criteria: criteria,
        executionTimeMs: 0,
      );
    }
    return MembreSearchResult.fromJson(data);
  }



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

      if (response.statusCode == 200) {
        return MembreCompletModel.fromJson(response.data as Map<String, dynamic>);
      } else if (response.statusCode == 404) {
        return null;
      } else {
        throw Exception('Erreur lors de la récupération du membre: ${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<MembreCompletModel> createMembre(MembreCompletModel membre) async {
    try {
      final response = await _apiClient.post(
        _baseUrl,
        data: membre.toJson(),
      );

      if (response.statusCode == 201 || response.statusCode == 200) {
        return MembreCompletModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la création du membre: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<MembreCompletModel> updateMembre(String id, MembreCompletModel membre) async {
    try {
      final response = await _apiClient.put(
        '$_baseUrl/$id',
        data: membre.toJson(),
      );

      if (response.statusCode == 200) {
        return MembreCompletModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la mise à jour du membre: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<void> deleteMembre(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 du membre: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<MembreCompletModel> activateMembre(String id) async {
    try {
      final response = await _apiClient.put('$_baseUrl/$id/activer');

      if (response.statusCode == 200) {
        return MembreCompletModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de l\'activation du membre: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<MembreCompletModel> deactivateMembre(String id) async {
    try {
      final response = await _apiClient.put('$_baseUrl/$id/desactiver');

      if (response.statusCode == 200) {
        return MembreCompletModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la désactivation du membre: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<MembreSearchResult> searchMembres({
    required MembreSearchCriteria criteria,
    int page = 0,
    int size = 20,
  }) async {
    try {
      // Les paramètres de pagination vont dans queryParameters
      // Les critères de recherche vont directement dans le body
      final response = await _apiClient.post(
        '$_baseUrl/search/advanced',
        queryParameters: {
          'page': page,
          'size': size,
        },
        data: criteria.toJson(),
      );

      return _parseMembreSearchResult(response, page, size, criteria);
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<MembreSearchResult> getActiveMembers({int page = 0, int size = 20}) async {
    // Utiliser la recherche avancée avec le critère statut=ACTIF
    return searchMembres(
      criteria: const MembreSearchCriteria(
        statut: 'ACTIF',
        includeInactifs: false,
      ),
      page: page,
      size: size,
    );
  }

  @override
  Future<MembreSearchResult> getBureauMembers({int page = 0, int size = 20}) async {
    // Utiliser la recherche avancée avec le critère membreBureau=true
    return searchMembres(
      criteria: const MembreSearchCriteria(
        membreBureau: true,
        statut: 'ACTIF',
      ),
      page: page,
      size: size,
    );
  }

  @override
  Future<Map<String, dynamic>> getMembresStats() 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<MembreCompletModel> resetMotDePasse(String id) async {
    try {
      final response = await _apiClient.put('$_baseUrl/$id/reinitialiser-mot-de-passe');

      if (response.statusCode == 200) {
        return MembreCompletModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la réinitialisation du mot de passe: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  @override
  Future<MembreCompletModel> affecterOrganisation(String membreId, String organisationId) async {
    try {
      final response = await _apiClient.put(
        '$_baseUrl/$membreId/affecter-organisation',
        queryParameters: {'organisationId': organisationId},
      );

      if (response.statusCode == 200) {
        return MembreCompletModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de l\'affectation à l\'organisation: ${response.statusCode}');
      }
    } on DioException {
      rethrow;
    } catch (e) {
      rethrow;
    }
  }

  // ── Cycle de vie des adhésions ────────────────────────────────────────────

  @override
  Future<Map<String, dynamic>> inviterMembre(
    String membreId,
    String organisationId, {
    String? roleOrg,
  }) async {
    final response = await _apiClient.put(
      '$_baseUrl/$membreId/inviter-organisation',
      queryParameters: {
        'organisationId': organisationId,
        if (roleOrg != null) 'roleOrg': roleOrg,
      },
    );
    if (response.statusCode == 200) {
      return Map<String, dynamic>.from(response.data as Map);
    }
    throw Exception('Invitation échouée: ${response.statusCode}');
  }

  @override
  Future<Map<String, dynamic>> activerAdhesion(
    String membreId,
    String organisationId, {
    String? motif,
  }) async {
    final response = await _apiClient.put(
      '$_baseUrl/$membreId/adhesion/activer',
      queryParameters: {'organisationId': organisationId},
      data: motif != null ? {'motif': motif} : <String, dynamic>{},
    );
    if (response.statusCode == 200) {
      return Map<String, dynamic>.from(response.data as Map);
    }
    throw Exception('Activation échouée: ${response.statusCode}');
  }

  @override
  Future<Map<String, dynamic>> suspendrAdhesion(
    String membreId,
    String organisationId, {
    String? motif,
  }) async {
    final response = await _apiClient.put(
      '$_baseUrl/$membreId/adhesion/suspendre',
      queryParameters: {'organisationId': organisationId},
      data: motif != null ? {'motif': motif} : <String, dynamic>{},
    );
    if (response.statusCode == 200) {
      return Map<String, dynamic>.from(response.data as Map);
    }
    throw Exception('Suspension échouée: ${response.statusCode}');
  }

  @override
  Future<Map<String, dynamic>> radierAdhesion(
    String membreId,
    String organisationId, {
    String? motif,
  }) async {
    final response = await _apiClient.put(
      '$_baseUrl/$membreId/adhesion/radier',
      queryParameters: {'organisationId': organisationId},
      data: motif != null ? {'motif': motif} : <String, dynamic>{},
    );
    if (response.statusCode == 200) {
      return Map<String, dynamic>.from(response.data as Map);
    }
    throw Exception('Radiation échouée: ${response.statusCode}');
  }
}

