import 'package:injectable/injectable.dart';
import 'package:dartz/dartz.dart';
import '../../domain/entities/dashboard_entity.dart';
import '../../domain/entities/compte_adherent_entity.dart';
import '../../domain/repositories/dashboard_repository.dart';
import '../datasources/dashboard_remote_datasource.dart';
import '../models/dashboard_stats_model.dart';
import '../models/membre_dashboard_synthese_model.dart';
import '../models/compte_adherent_model.dart';
import '../../../../core/error/exceptions.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/utils/logger.dart';
import '../../../../core/network/network_info.dart';

@LazySingleton(as: DashboardRepository)
class DashboardRepositoryImpl implements DashboardRepository {
  final DashboardRemoteDataSource remoteDataSource;
  final NetworkInfo networkInfo;

  DashboardRepositoryImpl({
    required this.remoteDataSource,
    required this.networkInfo,
  });

  @override
  Future<Either<Failure, CompteAdherentEntity>> getCompteAdherent() async {
    if (!await networkInfo.isConnected) {
      return const Left(NetworkFailure('No internet connection'));
    }
    try {
      final model = await remoteDataSource.getCompteAdherent();
      return Right(_mapCompteToEntity(model));
    } on NotFoundException {
      return const Left(NotFoundFailure(
        'Compte adhérent non trouvé',
        null,
        false,
        'Votre profil membre est en cours de création.',
      ));
    } on ServerException catch (e) {
      return Left(ServerFailure(e.message));
    } catch (e) {
      return Left(ServerFailure('Unexpected error: $e'));
    }
  }

  @override
  Future<Either<Failure, DashboardEntity>> getDashboardData(
    String organizationId,
    String userId, {
    bool useGlobalDashboard = false,
  }) async {
    if (!await networkInfo.isConnected) {
      return const Left(NetworkFailure('No internet connection'));
    }
    try {
      // Membre sans contexte org : utiliser l'API dashboard membre (GET /api/dashboard/membre/me)
      // useGlobalDashboard=true signifie qu'on veut les stats globales même sans orgId (ex: SUPER_ADMIN)
      final useMemberDashboard = !useGlobalDashboard && organizationId.trim().isEmpty;
      if (useMemberDashboard) {
        // Chargement parallèle de la synthèse et du compte adhérent unifié
        MembreDashboardSyntheseModel? synthese;
        CompteAdherentModel? compteModel;

        try {
          final results = await Future.wait([
            remoteDataSource.getMemberDashboardData(),
            remoteDataSource.getCompteAdherent(),
          ]);
          synthese = results[0] as MembreDashboardSyntheseModel;
          compteModel = results[1] as CompteAdherentModel;
        } on NotFoundException {
          // Le membre existe dans Keycloak mais pas encore en base → état intermédiaire
          AppLogger.info('DashboardRepository: membre non encore enregistré en base');
          return const Left(NotFoundFailure(
            'Profil membre non trouvé',
            null,
            false,
            'Votre profil membre est en cours de création.',
          ));
        }

        // Fallback : si les montants sont à zéro mais qu'il y a des cotisations,
        // on complète avec /api/cotisations/mes-cotisations/synthese
        Map<String, dynamic>? cotSynthese;
        if (synthese.totalCotisationsPayeesToutTemps == 0 ||
            synthese.tauxCotisationsPerso == null ||
            (synthese.tauxCotisationsPerso ?? 0) == 0) {
          cotSynthese = await remoteDataSource.getMesCotisationsSynthese();
        }

        return Right(_mapMemberSyntheseToEntity(
          synthese,
          userId,
          cotSynthese: cotSynthese,
          compteModel: compteModel,
        ));
      }

      final dashboardData = await remoteDataSource.getDashboardData(organizationId, userId);
      return Right(_mapToEntity(dashboardData));
    } on NotFoundException {
      return const Left(NotFoundFailure(
        'Profil membre non trouvé',
        null,
        false,
        'Votre profil membre est en cours de création.',
      ));
    } on ServerException catch (e) {
      return Left(ServerFailure(e.message));
    } catch (e) {
      return Left(ServerFailure('Unexpected error: $e'));
    }
  }

  /// Construit une DashboardEntity à partir de la synthèse membre.
  /// [cotSynthese] est optionnel : utilisé en fallback quand les montants du dashboard
  /// membre sont à zéro (incohérence backend entre /api/dashboard/membre/me
  /// et /api/cotisations/mes-cotisations/synthese).
  DashboardEntity _mapMemberSyntheseToEntity(
    MembreDashboardSyntheseModel s,
    String userId, {
    Map<String, dynamic>? cotSynthese,
    CompteAdherentModel? compteModel,
  }) {
    final now = DateTime.now();

    // ------------------------------------------------------------------
    // Montant des cotisations payées tout temps
    // ------------------------------------------------------------------
    double totalCotisationsToutTemps = s.totalCotisationsPayeesToutTemps;
    if (totalCotisationsToutTemps == 0 && cotSynthese != null) {
      // totalPayeAnnee = montant payé sur l'année en cours (meilleure approximation disponible)
      final totalPayeAnnee = _toDouble(cotSynthese['totalPayeAnnee']);
      if (totalPayeAnnee > 0) totalCotisationsToutTemps = totalPayeAnnee;
    }

    // ------------------------------------------------------------------
    // MON SOLDE TOTAL = cotisations payées + épargne
    // ------------------------------------------------------------------
    final monSoldeTotal = totalCotisationsToutTemps + s.monSoldeEpargne;

    // ------------------------------------------------------------------
    // Taux d'engagement (en %)
    // Priorité : tauxParticipationPerso > tauxCotisationsPerso > calculé depuis cotSynthese
    // ------------------------------------------------------------------
    int? tauxBrut = s.tauxParticipationPerso ?? s.tauxCotisationsPerso;
    double engagementRate = (tauxBrut ?? 0) / 100.0;
    if (engagementRate == 0 && cotSynthese != null) {
      final montantDu     = _toDouble(cotSynthese['montantDu']);
      final totalPayeAnnee = _toDouble(cotSynthese['totalPayeAnnee']);
      final total = montantDu + totalPayeAnnee;
      if (total > 0) engagementRate = totalPayeAnnee / total;
    }

    // ------------------------------------------------------------------
    // Nombre de cotisations — utilize NEW nombreCotisationsTotal if available
    // ------------------------------------------------------------------
    final int nombreCotisations = s.nombreCotisationsTotal > 0 
        ? s.nombreCotisationsTotal 
        : s.nombreCotisationsPayees;

    final stats = DashboardStatsEntity(
      totalMembers: 0,
      activeMembers: 0,
      totalEvents: 0,
      upcomingEvents: s.evenementsAVenir,
      totalContributions: nombreCotisations,
      totalContributionAmount: monSoldeTotal,
      contributionsAmountOnly: totalCotisationsToutTemps,
      pendingRequests: 0,
      completedProjects: 0,
      monthlyGrowth: s.evolutionEpargneNombre,
      engagementRate: engagementRate,
      lastUpdated: now,
      totalOrganizations: null,
      organizationTypeDistribution: null,
    );
    return DashboardEntity(
      stats: stats,
      recentActivities: const [],
      upcomingEvents: const [],
      userPreferences: const <String, dynamic>{},
      organizationId: '',
      userId: userId,
      monCompte: compteModel != null ? _mapCompteToEntity(compteModel) : null,
    );

  }


  static double _toDouble(dynamic v) {
    if (v == null) return 0;
    if (v is num) return v.toDouble();
    if (v is String) return double.tryParse(v) ?? 0;
    return 0;
  }

  @override
  Future<Either<Failure, DashboardStatsEntity>> getDashboardStats(
    String organizationId,
    String userId,
  ) async {
    if (await networkInfo.isConnected) {
      try {
        final stats = await remoteDataSource.getDashboardStats(organizationId, userId);
        return Right(_mapStatsToEntity(stats));
      } on ServerException catch (e) {
        return Left(ServerFailure(e.message));
      } catch (e) {
        return Left(ServerFailure('Unexpected error: $e'));
      }
    } else {
      return const Left(NetworkFailure('No internet connection'));
    }
  }

  @override
  Future<Either<Failure, List<RecentActivityEntity>>> getRecentActivities(
    String organizationId,
    String userId, {
    int limit = 10,
  }) async {
    if (await networkInfo.isConnected) {
      try {
        final activities = await remoteDataSource.getRecentActivities(
          organizationId,
          userId,
          limit: limit,
        );
        return Right(activities.map(_mapActivityToEntity).toList());
      } on ServerException catch (e) {
        return Left(ServerFailure(e.message));
      } catch (e) {
        return Left(ServerFailure('Unexpected error: $e'));
      }
    } else {
      return const Left(NetworkFailure('No internet connection'));
    }
  }

  @override
  Future<Either<Failure, List<UpcomingEventEntity>>> getUpcomingEvents(
    String organizationId,
    String userId, {
    int limit = 5,
  }) async {
    if (await networkInfo.isConnected) {
      try {
        final events = await remoteDataSource.getUpcomingEvents(
          organizationId,
          userId,
          limit: limit,
        );
        return Right(events.map(_mapEventToEntity).toList());
      } on ServerException catch (e) {
        return Left(ServerFailure(e.message));
      } catch (e) {
        return Left(ServerFailure('Unexpected error: $e'));
      }
    } else {
      return const Left(NetworkFailure('No internet connection'));
    }
  }

  CompteAdherentEntity _mapCompteToEntity(CompteAdherentModel model) {
    return CompteAdherentEntity(
      numeroMembre: model.numeroMembre,
      nomComplet: model.nomComplet,
      organisationNom: model.organisationNom,
      dateAdhesion: model.dateAdhesion != null ? DateTime.tryParse(model.dateAdhesion!) : null,
      statutCompte: model.statutCompte,
      soldeCotisations: model.soldeCotisations,
      soldeEpargne: model.soldeEpargne,
      soldeBloque: model.soldeBloque,
      soldeTotalDisponible: model.soldeTotalDisponible,
      encoursCreditTotal: model.encoursCreditTotal,
      capaciteEmprunt: model.capaciteEmprunt,
      nombreCotisationsPayees: model.nombreCotisationsPayees,
      nombreCotisationsTotal: model.nombreCotisationsTotal,
      nombreCotisationsEnRetard: model.nombreCotisationsEnRetard,
      engagementRate: (model.tauxEngagement ?? 0) / 100.0,
      nombreComptesEpargne: model.nombreComptesEpargne,
      dateCalcul: DateTime.tryParse(model.dateCalcul) ?? DateTime.now(),
    );
  }

  // Mappers
  DashboardEntity _mapToEntity(DashboardDataModel model) {
    return DashboardEntity(
      stats: _mapStatsToEntity(model.stats),
      recentActivities: model.recentActivities.map(_mapActivityToEntity).toList(),
      upcomingEvents: model.upcomingEvents.map(_mapEventToEntity).toList(),
      userPreferences: model.userPreferences,
      organizationId: model.organizationId,
      userId: model.userId,
    );
  }

  DashboardStatsEntity _mapStatsToEntity(DashboardStatsModel model) {
    return DashboardStatsEntity(
      totalMembers: model.totalMembers,
      activeMembers: model.activeMembers,
      totalEvents: model.totalEvents,
      upcomingEvents: model.upcomingEvents,
      totalContributions: model.totalContributions,
      totalContributionAmount: model.totalContributionAmount,
      contributionsAmountOnly: null,
      pendingRequests: model.pendingRequests,
      completedProjects: model.completedProjects,
      monthlyGrowth: model.monthlyGrowth,
      engagementRate: model.engagementRate,
      lastUpdated: model.lastUpdated,
      totalOrganizations: model.totalOrganizations,
      organizationTypeDistribution: model.organizationTypeDistribution,
    );
  }

  RecentActivityEntity _mapActivityToEntity(RecentActivityModel model) {
    return RecentActivityEntity(
      id: model.id,
      type: model.type,
      title: model.title,
      description: model.description,
      userAvatar: model.userAvatar,
      userName: model.userName,
      timestamp: model.timestamp,
      actionUrl: model.actionUrl,
      metadata: model.metadata,
    );
  }

  UpcomingEventEntity _mapEventToEntity(UpcomingEventModel model) {
    return UpcomingEventEntity(
      id: model.id,
      title: model.title,
      description: model.description,
      startDate: model.startDate,
      endDate: model.endDate,
      location: model.location,
      maxParticipants: model.maxParticipants,
      currentParticipants: model.currentParticipants,
      status: model.status,
      imageUrl: model.imageUrl,
      tags: model.tags,
    );
  }
}
