/// Repository pour la gestion des organisations
/// Interface avec l'API backend OrganizationResource
library organization_repository;

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

/// Interface du repository des organisations
abstract class OrganizationRepository {
  /// Récupère la liste des organisations avec pagination
  Future<List<OrganizationModel>> getOrganizations({
    int page = 0,
    int size = 20,
    String? recherche,
  });

  /// Récupère une organisation par son ID
  Future<OrganizationModel?> getOrganizationById(String id);

  /// Crée une nouvelle organisation
  Future<OrganizationModel> createOrganization(OrganizationModel organization);

  /// Met à jour une organisation
  Future<OrganizationModel> updateOrganization(String id, OrganizationModel organization);

  /// Supprime une organisation
  Future<void> deleteOrganization(String id);

  /// Active une organisation
  Future<OrganizationModel> activateOrganization(String id);

  /// Recherche avancée d'organisations
  Future<List<OrganizationModel>> searchOrganizations({
    String? nom,
    TypeOrganization? type,
    StatutOrganization? statut,
    String? ville,
    String? region,
    String? pays,
    int page = 0,
    int size = 20,
  });

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

/// Implémentation du repository des organisations
class OrganizationRepositoryImpl implements OrganizationRepository {
  final Dio _dio;
  static const String _baseUrl = '/api/organisations';

  OrganizationRepositoryImpl(this._dio);

  @override
  Future<List<OrganizationModel>> getOrganizations({
    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) {
        final List<dynamic> data = response.data as List<dynamic>;
        return data
            .map((json) => OrganizationModel.fromJson(json as Map<String, dynamic>))
            .toList();
      } else {
        throw Exception('Erreur lors de la récupération des organisations: ${response.statusCode}');
      }
    } on DioException catch (e) {
      // En cas d'erreur réseau, retourner des données de démonstration
      print('Erreur API, utilisation des données de démonstration: ${e.message}');
      return _getMockOrganizations(page: page, size: size, recherche: recherche);
    } catch (e) {
      // En cas d'erreur inattendue, retourner des données de démonstration
      print('Erreur inattendue, utilisation des données de démonstration: $e');
      return _getMockOrganizations(page: page, size: size, recherche: recherche);
    }
  }

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

      if (response.statusCode == 200) {
        return OrganizationModel.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\'organisation: ${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\'organisation: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la récupération de l\'organisation: $e');
    }
  }

  @override
  Future<OrganizationModel> createOrganization(OrganizationModel organization) async {
    try {
      final response = await _dio.post(
        _baseUrl,
        data: organization.toJson(),
      );

      if (response.statusCode == 201) {
        return OrganizationModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la création de l\'organisation: ${response.statusCode}');
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 400) {
        final errorData = e.response?.data;
        if (errorData is Map<String, dynamic> && errorData.containsKey('error')) {
          throw Exception('Données invalides: ${errorData['error']}');
        }
      } else if (e.response?.statusCode == 409) {
        throw Exception('Une organisation avec ces informations existe déjà');
      }
      throw Exception('Erreur réseau lors de la création de l\'organisation: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la création de l\'organisation: $e');
    }
  }

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

      if (response.statusCode == 200) {
        return OrganizationModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de la mise à jour de l\'organisation: ${response.statusCode}');
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Organisation non trouvée');
      } else if (e.response?.statusCode == 400) {
        final errorData = e.response?.data;
        if (errorData is Map<String, dynamic> && errorData.containsKey('error')) {
          throw Exception('Données invalides: ${errorData['error']}');
        }
      }
      throw Exception('Erreur réseau lors de la mise à jour de l\'organisation: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la mise à jour de l\'organisation: $e');
    }
  }

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

      if (response.statusCode != 200 && response.statusCode != 204) {
        throw Exception('Erreur lors de la suppression de l\'organisation: ${response.statusCode}');
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Organisation non trouvée');
      } else if (e.response?.statusCode == 400) {
        final errorData = e.response?.data;
        if (errorData is Map<String, dynamic> && errorData.containsKey('error')) {
          throw Exception('Impossible de supprimer: ${errorData['error']}');
        }
      }
      throw Exception('Erreur réseau lors de la suppression de l\'organisation: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la suppression de l\'organisation: $e');
    }
  }

  @override
  Future<OrganizationModel> activateOrganization(String id) async {
    try {
      final response = await _dio.post('$_baseUrl/$id/activer');

      if (response.statusCode == 200) {
        return OrganizationModel.fromJson(response.data as Map<String, dynamic>);
      } else {
        throw Exception('Erreur lors de l\'activation de l\'organisation: ${response.statusCode}');
      }
    } on DioException catch (e) {
      if (e.response?.statusCode == 404) {
        throw Exception('Organisation non trouvée');
      }
      throw Exception('Erreur réseau lors de l\'activation de l\'organisation: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de l\'activation de l\'organisation: $e');
    }
  }

  @override
  Future<List<OrganizationModel>> searchOrganizations({
    String? nom,
    TypeOrganization? type,
    StatutOrganization? statut,
    String? ville,
    String? region,
    String? pays,
    int page = 0,
    int size = 20,
  }) async {
    try {
      final queryParams = <String, dynamic>{
        'page': page,
        'size': size,
      };

      if (nom?.isNotEmpty == true) queryParams['nom'] = nom;
      if (type != null) queryParams['type'] = type.name.toUpperCase();
      if (statut != null) queryParams['statut'] = statut.name.toUpperCase();
      if (ville?.isNotEmpty == true) queryParams['ville'] = ville;
      if (region?.isNotEmpty == true) queryParams['region'] = region;
      if (pays?.isNotEmpty == true) queryParams['pays'] = pays;

      final response = await _dio.get(
        '$_baseUrl/recherche',
        queryParameters: queryParams,
      );

      if (response.statusCode == 200) {
        final List<dynamic> data = response.data as List<dynamic>;
        return data
            .map((json) => OrganizationModel.fromJson(json as Map<String, dynamic>))
            .toList();
      } else {
        throw Exception('Erreur lors de la recherche d\'organisations: ${response.statusCode}');
      }
    } on DioException catch (e) {
      throw Exception('Erreur réseau lors de la recherche d\'organisations: ${e.message}');
    } catch (e) {
      throw Exception('Erreur inattendue lors de la recherche d\'organisations: $e');
    }
  }

  @override
  Future<Map<String, dynamic>> getOrganizationsStats() 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');
    }
  }

  /// Données de démonstration pour le développement
  List<OrganizationModel> _getMockOrganizations({
    int page = 0,
    int size = 20,
    String? recherche,
  }) {
    final mockData = [
      OrganizationModel(
        id: '1',
        nom: 'Syndicat des Travailleurs Unis',
        nomCourt: 'STU',
        description: 'Organisation syndicale représentant les travailleurs de l\'industrie',
        typeOrganisation: TypeOrganization.syndicat,
        statut: StatutOrganization.active,
        adresse: '123 Rue de la République',
        ville: 'Paris',
        codePostal: '75001',
        region: 'Île-de-France',
        pays: 'France',
        telephone: '+33 1 23 45 67 89',
        email: 'contact@stu.fr',
        siteWeb: 'https://www.stu.fr',
        nombreMembres: 1250,
        budgetAnnuel: 500000.0,
        montantCotisationAnnuelle: 120.0,
        dateCreation: DateTime(2020, 1, 15),
        dateModification: DateTime.now(),
      ),
      OrganizationModel(
        id: '2',
        nom: 'Association des Professionnels de la Santé',
        nomCourt: 'APS',
        description: 'Association regroupant les professionnels du secteur médical',
        typeOrganisation: TypeOrganization.association,
        statut: StatutOrganization.active,
        adresse: '456 Avenue de la Santé',
        ville: 'Lyon',
        codePostal: '69000',
        region: 'Auvergne-Rhône-Alpes',
        pays: 'France',
        telephone: '+33 4 78 90 12 34',
        email: 'info@aps-sante.fr',
        siteWeb: 'https://www.aps-sante.fr',
        nombreMembres: 850,
        budgetAnnuel: 300000.0,
        montantCotisationAnnuelle: 80.0,
        dateCreation: DateTime(2019, 6, 10),
        dateModification: DateTime.now(),
      ),
      OrganizationModel(
        id: '3',
        nom: 'Coopérative Agricole du Sud',
        nomCourt: 'CAS',
        description: 'Coopérative regroupant les agriculteurs de la région Sud',
        typeOrganisation: TypeOrganization.cooperative,
        statut: StatutOrganization.active,
        adresse: '789 Route des Champs',
        ville: 'Marseille',
        codePostal: '13000',
        region: 'Provence-Alpes-Côte d\'Azur',
        pays: 'France',
        telephone: '+33 4 91 23 45 67',
        email: 'contact@cas-agricole.fr',
        siteWeb: 'https://www.cas-agricole.fr',
        nombreMembres: 420,
        budgetAnnuel: 750000.0,
        montantCotisationAnnuelle: 200.0,
        dateCreation: DateTime(2018, 3, 20),
        dateModification: DateTime.now(),
      ),
      OrganizationModel(
        id: '4',
        nom: 'Fédération des Artisans',
        nomCourt: 'FA',
        description: 'Fédération représentant les artisans de tous secteurs',
        typeOrganisation: TypeOrganization.fondation,
        statut: StatutOrganization.inactive,
        adresse: '321 Rue de l\'Artisanat',
        ville: 'Toulouse',
        codePostal: '31000',
        region: 'Occitanie',
        pays: 'France',
        telephone: '+33 5 61 78 90 12',
        email: 'secretariat@federation-artisans.fr',
        siteWeb: 'https://www.federation-artisans.fr',
        nombreMembres: 680,
        budgetAnnuel: 400000.0,
        montantCotisationAnnuelle: 150.0,
        dateCreation: DateTime(2017, 9, 5),
        dateModification: DateTime.now(),
      ),
      OrganizationModel(
        id: '5',
        nom: 'Union des Commerçants',
        nomCourt: 'UC',
        description: 'Union regroupant les commerçants locaux',
        typeOrganisation: TypeOrganization.entreprise,
        statut: StatutOrganization.active,
        adresse: '654 Boulevard du Commerce',
        ville: 'Bordeaux',
        codePostal: '33000',
        region: 'Nouvelle-Aquitaine',
        pays: 'France',
        telephone: '+33 5 56 34 12 78',
        email: 'contact@union-commercants.fr',
        siteWeb: 'https://www.union-commercants.fr',
        nombreMembres: 320,
        budgetAnnuel: 180000.0,
        montantCotisationAnnuelle: 90.0,
        dateCreation: DateTime(2021, 11, 12),
        dateModification: DateTime.now(),
      ),
    ];

    // Filtrer par recherche si nécessaire
    List<OrganizationModel> filteredData = mockData;
    if (recherche?.isNotEmpty == true) {
      final query = recherche!.toLowerCase();
      filteredData = mockData.where((org) =>
        org.nom.toLowerCase().contains(query) ||
        (org.nomCourt?.toLowerCase().contains(query) ?? false) ||
        (org.description?.toLowerCase().contains(query) ?? false) ||
        (org.ville?.toLowerCase().contains(query) ?? false)
      ).toList();
    }

    // Pagination
    final startIndex = page * size;
    final endIndex = (startIndex + size).clamp(0, filteredData.length);

    if (startIndex >= filteredData.length) {
      return [];
    }

    return filteredData.sublist(startIndex, endIndex);
  }
}
