import logging
from django.utils import timezone
from django.shortcuts import get_object_or_404
from django.db.models import Count
from django.db.models.functions import TruncMonth
from django.contrib.auth import get_user_model

from rest_framework import status, generics, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

# --- IMPORTS DES MODÈLES DES AUTRES APPS ---
from formations.models import UserProgress, Formation
from market.models import MarketSurvey, SurveyResponse
from market.serializers import SurveyResponseSerializer
from business_plan.models import BusinessPlan
from tools.models import BusinessModelCanvas, PitchDeck

# Même seuil que market/views.py (REQUIRED_RESPONSES)
MARKET_REQUIRED_RESPONSES = 2

# --- IMPORTS LOCAUX ---
# Au début de users/views.py
from .serializers import (
    UserSerializer,
    AdminUserListSerializer,
    AdminUserDetailSerializer,
    InviteUserSerializer,
    InviteMentorSerializer,
    RegisterSerializer,
    ActivateAccountSerializer,
    ForgotPasswordSerializer,
    ResetPasswordSerializer,
    CustomTokenObtainPairSerializer,
)

from .utils import (
    generate_otp,
    send_otp_email,
    send_register_pending_email,
    send_account_activated_email,
    send_password_reset_email,
    send_mentor_changed_to_student_email,
    send_new_student_assigned_to_mentor_email,
)

User = get_user_model()

# --- 1. LOGIN PERSONNALISÉ ---
class CustomTokenObtainPairView(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer

# --- 2. INVITATION UTILISATEUR (ADMIN) ---
class InviteUserView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def post(self, request):
        serializer = InviteUserSerializer(data=request.data)
        if serializer.is_valid():
            email = serializer.validated_data['email']
            if User.objects.filter(email=email).exists():
                return Response({"error": "Cet email existe déjà."}, status=status.HTTP_400_BAD_REQUEST)
            
            user = serializer.save()
            otp = generate_otp()
            user.otp_code = otp
            user.otp_created_at = timezone.now()
            user.save()
            
            email_sent = send_otp_email(email, otp)
            if email_sent:
                return Response({
                    "message": "Utilisateur invité avec succès. Email envoyé.",
                    "user": serializer.data
                }, status=status.HTTP_201_CREATED)
            else:
                return Response({"message": "Utilisateur créé mais échec de l'envoi email."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


# --- 2a. INVITATION MENTOR (ADMIN) ---
class InviteMentorView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def post(self, request):
        serializer = InviteMentorSerializer(data=request.data)
        if serializer.is_valid():
            email = serializer.validated_data['email']
            if User.objects.filter(email=email).exists():
                return Response({"error": "Cet email existe déjà."}, status=status.HTTP_400_BAD_REQUEST)
            user = serializer.save()
            otp = generate_otp()
            user.otp_code = otp
            user.otp_created_at = timezone.now()
            user.save()
            email_sent = send_otp_email(email, otp)
            if email_sent:
                return Response({
                    "message": "Mentor invité avec succès. Email envoyé.",
                    "user": serializer.data,
                }, status=status.HTTP_201_CREATED)
            return Response({"message": "Mentor créé mais échec de l'envoi email."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


# --- 2b. INSCRIPTION PUBLIQUE (compte inactif, admin active) ---
class RegisterView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = RegisterSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        user = serializer.save()
        send_register_pending_email(user.email, user.first_name)
        return Response(
            {"message": "Compte créé avec succès. Un email de confirmation vous a été envoyé. Votre compte sera activé par l'équipe GOON ; vous recevrez un email lorsqu'il sera actif."},
            status=status.HTTP_201_CREATED,
        )

# --- 3. ACTIVATION COMPTE ---
class ActivateAccountView(APIView):
    """
    Active le compte avec email + code (token) reçu par email.
    - Le code expire après 24h (otp_created_at).
    - Une fois le compte activé, le code est invalidé (otp_code mis à None) :
      le lien et le code ne sont plus valables.
    """
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = ActivateAccountSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        email = serializer.validated_data['email']
        otp = serializer.validated_data['otp_code']
        password = serializer.validated_data['password']

        try:
            user = User.objects.get(email=email)
        except User.DoesNotExist:
            return Response(
                {"error": "Utilisateur introuvable. Utilisez le lien reçu par email."},
                status=status.HTTP_404_NOT_FOUND,
            )

        if user.is_verified:
            return Response(
                {"error": "Ce compte est déjà activé. Le lien n'est plus valable. Connectez-vous avec votre mot de passe."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if not user.otp_code:
            return Response(
                {"error": "Ce lien a déjà été utilisé. Le compte est activé. Connectez-vous avec votre mot de passe."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        if user.otp_code != otp:
            return Response(
                {"error": "Code invalide. Vérifiez le code reçu par email ou utilisez le lien dans l'email."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Expiration 24h
        from datetime import timedelta
        if user.otp_created_at and timezone.now() - user.otp_created_at > timedelta(hours=24):
            user.otp_code = None
            user.otp_created_at = None
            user.save(update_fields=['otp_code', 'otp_created_at'])
            return Response(
                {"error": "Ce lien et ce code ont expiré (validité 24h). Contactez l'administrateur pour une nouvelle invitation."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        user.set_password(password)
        user.otp_code = None
        user.otp_created_at = None
        user.is_verified = True
        user.save(update_fields=['password', 'otp_code', 'otp_created_at', 'is_verified'])

        return Response({"message": "Compte activé avec succès."}, status=status.HTTP_200_OK)

# --- 3b. MOT DE PASSE OUBLIÉ (demande) ---
logger = logging.getLogger(__name__)


class ForgotPasswordView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = ForgotPasswordSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        email = serializer.validated_data["email"]
        try:
            user = User.objects.get(email__iexact=email)
        except User.DoesNotExist:
            logger.info("Forgot password: aucun compte pour email=%s", email)
            return Response(
                {"message": "Si un compte existe avec cet email, un lien et un code de réinitialisation vous ont été envoyés. Pensez à vérifier vos spams."},
                status=status.HTTP_200_OK,
            )
        if not user.is_verified:
            logger.warning(
                "Forgot password: compte non activé pour %s (is_verified=False). "
                "Seuls les comptes déjà activés peuvent réinitialiser leur mot de passe.",
                email,
            )
            return Response(
                {"message": "Si un compte existe avec cet email, un lien et un code de réinitialisation vous ont été envoyés. Pensez à vérifier vos spams."},
                status=status.HTTP_200_OK,
            )
        otp = generate_otp()
        user.otp_code = otp
        user.otp_created_at = timezone.now()
        user.save(update_fields=["otp_code", "otp_created_at"])
        sent = send_password_reset_email(user.email, otp)
        if sent:
            logger.info("Forgot password: email de réinitialisation envoyé à %s", email)
        else:
            logger.error("Forgot password: échec envoi email à %s (vérifier SMTP / logs)", email)
        return Response(
            {"message": "Si un compte existe avec cet email, un lien et un code de réinitialisation vous ont été envoyés. Pensez à vérifier vos spams."},
            status=status.HTTP_200_OK,
        )

# --- 3c. RÉINITIALISATION MOT DE PASSE (code + nouveau MDP) ---
class ResetPasswordView(APIView):
    permission_classes = [permissions.AllowAny]

    def post(self, request):
        serializer = ResetPasswordSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        email = serializer.validated_data["email"]
        otp = serializer.validated_data["otp_code"]
        password = serializer.validated_data["password"]
        try:
            user = User.objects.get(email__iexact=email)
        except User.DoesNotExist:
            return Response(
                {"error": "Utilisateur introuvable."},
                status=status.HTTP_404_NOT_FOUND,
            )
        if not user.is_verified:
            return Response(
                {"error": "Ce compte n'est pas encore activé. Utilisez le lien d'activation reçu par email."},
                status=status.HTTP_400_BAD_REQUEST,
            )
        if not user.otp_code or user.otp_code != otp:
            return Response(
                {"error": "Code invalide ou déjà utilisé. Demandez une nouvelle réinitialisation."},
                status=status.HTTP_400_BAD_REQUEST,
            )
        from datetime import timedelta
        if user.otp_created_at and timezone.now() - user.otp_created_at > timedelta(hours=24):
            user.otp_code = None
            user.otp_created_at = None
            user.save(update_fields=["otp_code", "otp_created_at"])
            return Response(
                {"error": "Ce code a expiré (validité 24h). Demandez une nouvelle réinitialisation."},
                status=status.HTTP_400_BAD_REQUEST,
            )
        user.set_password(password)
        user.otp_code = None
        user.otp_created_at = None
        user.save(update_fields=["password", "otp_code", "otp_created_at"])
        return Response({"message": "Mot de passe mis à jour. Vous pouvez vous connecter."}, status=status.HTTP_200_OK)

# --- 4. PROFIL (CONNECTÉ) ---
class UserProfileView(generics.RetrieveUpdateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    serializer_class = UserSerializer

    def get_object(self):
        return self.request.user

# --- 5. STATISTIQUES GLOBALES (ADMIN) ---
class GlobalStatsView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get(self, request):
        from formations.models import UserFormationStatus, LessonQuizAttempt
        from django.utils import timezone
        from datetime import timedelta

        total_users = User.objects.filter(role='USER').count()
        completed_lessons_count = UserProgress.objects.filter(is_completed=True).count()
        total_bp = BusinessPlan.objects.count()
        total_bmc = BusinessModelCanvas.objects.count()
        total_pitch = PitchDeck.objects.count()

        # Formations : terminées (users avec toutes formations validées) vs en cours vs non démarrées
        all_formations = Formation.objects.count()
        users_completed_all = UserFormationStatus.objects.values('user').annotate(
            n=Count('formation')
        ).filter(n=all_formations).count() if all_formations else 0
        users_in_progress = UserFormationStatus.objects.values('user').distinct().count() - users_completed_all
        users_not_started = max(0, total_users - users_completed_all - users_in_progress)

        # Croissance mensuelle
        user_growth_qs = User.objects.filter(role='USER').annotate(
            month=TruncMonth('date_joined')
        ).values('month').annotate(c=Count('id')).order_by('month')
        months = [g['month'].strftime('%b') if g['month'] else "N/A" for g in user_growth_qs]
        user_growth = [g['c'] for g in user_growth_qs]

        # Activité récente
        now = timezone.now()
        start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
        active_this_month = User.objects.filter(role='USER', date_joined__gte=start_of_month).count()
        start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0)
        active_today = User.objects.filter(role='USER', date_joined__gte=start_of_day).count()
        total_quizzes = LessonQuizAttempt.objects.count()

        # Taux d'engagement (réel si possible)
        engagement_rate = round((total_bp / total_users * 100), 1) if total_users else 0
        engagement_rate = min(100, engagement_rate)

        return Response({
            "total_users": total_users,
            "completed_formations": completed_lessons_count,
            "ongoing_formations": users_in_progress,
            "total_bp": total_bp,
            "total_bmc": total_bmc,
            "total_pitch": total_pitch,
            "engagement_rate": engagement_rate,
            "months": months,
            "user_growth": user_growth,
            "formation_distribution": {
                "labels": ["En cours", "Terminées", "Non démarrées"],
                "values": [users_in_progress, users_completed_all, users_not_started],
            },
            "documents_distribution": {
                "labels": ["Business Plan", "BMC", "Pitch Deck"],
                "values": [total_bp, total_bmc, total_pitch],
            },
            "active_this_month": active_this_month,
            "active_today": active_today,
            "total_quizzes": total_quizzes,
            "popular_formations_count": all_formations,
        })

# --- 6. LISTE USERS (ADMIN) ---
class AdminUserListView(generics.ListAPIView):
    serializer_class = AdminUserListSerializer
    permission_classes = [permissions.IsAdminUser]

    def get_queryset(self):
        qs = User.objects.filter(role='USER').order_by('-date_joined')
        mentor_id = self.request.query_params.get('mentor_id')
        if mentor_id:
            try:
                qs = qs.filter(mentor_id=int(mentor_id))
            except ValueError:
                pass
        return qs


# --- 6c. LISTE MENTORS (ADMIN) : avec nombre d'étudiants (dropdown + page Mentors) ---
class AdminMentorListView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get(self, request):
        mentors = (
            User.objects.filter(role=User.Role.MENTOR, is_active=True)
            .annotate(student_count=Count('mentored_students'))
            .order_by('first_name', 'last_name')
        )
        data = [
            {
                "id": u.id,
                "email": u.email,
                "first_name": u.first_name,
                "last_name": u.last_name,
                "student_count": u.student_count,
            }
            for u in mentors
        ]
        return Response(data)


# --- 6b. DÉTAIL USER (ADMIN) : GET / PATCH / DELETE ---
class AdminUserDetailView(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get_object(self, pk):
        return get_object_or_404(User, pk=pk)

    def get(self, request, pk):
        user = self.get_object(pk)
        serializer = AdminUserDetailSerializer(user)
        return Response(serializer.data)

    def patch(self, request, pk):
        user = self.get_object(pk)
        was_inactive = user.is_active is False
        old_mentor_id = user.mentor_id
        serializer = AdminUserDetailSerializer(user, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            if was_inactive and user.is_active:
                send_account_activated_email(user.email, user.first_name)
            # Changement de mentor : notifier l'étudiant et le nouveau mentor
            new_mentor = user.mentor
            if user.role == User.Role.USER and (old_mentor_id != (new_mentor.id if new_mentor else None)):
                if new_mentor:
                    send_mentor_changed_to_student_email(
                        user.email,
                        user.first_name,
                        f"{new_mentor.first_name} {new_mentor.last_name}".strip() or new_mentor.email,
                    )
                    send_new_student_assigned_to_mentor_email(
                        new_mentor.email,
                        new_mentor.first_name,
                        f"{user.first_name} {user.last_name}".strip() or user.email,
                    )
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        user = self.get_object(pk)
        user.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


# --- 7. VUE 360° DÉTAILLÉE (ADMIN) ---
class UserDetail360View(APIView):
    permission_classes = [permissions.IsAdminUser]

    def get(self, request, pk):
        user = get_object_or_404(User, pk=pk)
        
        # 1. Détails Formations
        formations_data = []
        all_formations = Formation.objects.all()
        for f in all_formations:
            # On calcule le % de complétion basé sur les leçons de cette formation
            total_lessons = f.lessons.count()
            completed_lessons = UserProgress.objects.filter(
                user=user, 
                lesson__formation=f, 
                is_completed=True
            ).count()
            
            progress = (completed_lessons / total_lessons * 100) if total_lessons > 0 else 0
            
            formations_data.append({
                "id": f.id,
                "title": f.title,
                "progress": round(progress, 2),
                "is_validated": progress == 100
            })

        # 2. État Étude de Marché (compteur + stats pour graphiques + liste des réponses)
        study = MarketSurvey.objects.filter(entrepreneur=user).first()
        market_responses_count = study.responses.count() if study else 0
        market_required_responses = MARKET_REQUIRED_RESPONSES
        market_stats = None
        market_responses = []
        if study:
            responses_qs = study.responses.all().order_by('-created_at')
            market_responses = SurveyResponseSerializer(responses_qs, many=True).data
            age_stats = study.responses.values('age_range').annotate(count=Count('id'))
            gender_stats = study.responses.values('gender').annotate(count=Count('id'))
            interest_stats = study.responses.values('interest_level').annotate(count=Count('id'))
            price_stats = study.responses.values('general_price_perception').annotate(count=Count('id'))
            location_stats = study.responses.values('location').annotate(count=Count('id'))
            market_stats = {
                "total_responses": market_responses_count,
                "required_responses": market_required_responses,
                "responses_needed": max(0, market_required_responses - market_responses_count),
                "is_unlocked": market_responses_count >= market_required_responses,
                "age_distribution": {item['age_range']: item['count'] for item in age_stats},
                "gender_distribution": {item['gender']: item['count'] for item in gender_stats},
                "interest_distribution": {item['interest_level']: item['count'] for item in interest_stats},
                "price_perception": {item['general_price_perception']: item['count'] for item in price_stats},
                "location_distribution": {item['location']: item['count'] for item in location_stats},
            }

        # 3. État Documents (BP, BMC, Pitch)
        has_bp = BusinessPlan.objects.filter(entrepreneur=user).exists()
        bp_id = BusinessPlan.objects.filter(entrepreneur=user).values_list('id', flat=True).first() if has_bp else None
        has_bmc = BusinessModelCanvas.objects.filter(entrepreneur=user).exists()
        bmc_id = BusinessModelCanvas.objects.filter(entrepreneur=user).values_list('id', flat=True).first() if has_bmc else None
        has_pitch = PitchDeck.objects.filter(entrepreneur=user).exists()
        pitch_id = PitchDeck.objects.filter(entrepreneur=user).values_list('id', flat=True).first() if has_pitch else None

        return Response({
            "user": {
                "id": user.id,
                "first_name": user.first_name,
                "last_name": user.last_name,
                "email": user.email,
                "phone": user.phone,
                "date_joined": user.date_joined.strftime("%d/%m/%Y"),
                "is_verified": user.is_verified,
                "is_active": user.is_active,
                "is_staff": user.is_staff,
                "is_superuser": user.is_superuser,
                "role": user.role,
                "location": getattr(user, "location", None) or None,
                "mentor_id": user.mentor_id,
                "mentor": (
                    {
                        "id": user.mentor.id,
                        "email": user.mentor.email,
                        "first_name": user.mentor.first_name,
                        "last_name": user.mentor.last_name,
                    }
                    if user.mentor else None
                ),
            },
            "global_progress": round(sum(f['progress'] for f in formations_data) / len(all_formations), 2) if all_formations else 0,
            "formations": formations_data,
            "market_responses_count": market_responses_count,
            "market_required_responses": market_required_responses,
            "market_stats": market_stats,
            "market_responses": market_responses,
            "has_bp": has_bp,
            "bp_id": bp_id,
            "has_bmc": has_bmc,
            "bmc_id": bmc_id,
            "has_pitch": has_pitch,
            "pitch_id": pitch_id,
        })


# --- 8. API MENTOR : liste des étudiants assignés + détail 360 ---
class IsMentor(permissions.BasePermission):
    """Accès réservé aux utilisateurs avec role=MENTOR."""
    def has_permission(self, request, view):
        return request.user.is_authenticated and getattr(request.user, 'role', None) == User.Role.MENTOR


class MentorStatsView(APIView):
    """Stats dashboard mentor : nombre d'étudiants, activité récente (threads mis à jour)."""
    permission_classes = [permissions.IsAuthenticated, IsMentor]

    def get(self, request):
        from mentoring.models import MentoringThread
        student_count = User.objects.filter(mentor=request.user, role=User.Role.USER).count()
        recent_threads = (
            MentoringThread.objects.filter(student__mentor=request.user)
            .select_related('student')
            .order_by('-updated_at')[:10]
        )
        recent_activity = [
            {
                "thread_id": t.id,
                "student_id": t.student_id,
                "student_name": f"{t.student.first_name} {t.student.last_name}".strip() or t.student.email,
                "document_type": t.document_type,
                "status": t.status,
                "updated_at": t.updated_at.isoformat() if t.updated_at else None,
            }
            for t in recent_threads
        ]
        return Response({
            "student_count": student_count,
            "recent_activity": recent_activity,
        })


class MentorStudentListView(generics.ListAPIView):
    """Liste des étudiants assignés au mentor connecté."""
    permission_classes = [permissions.IsAuthenticated, IsMentor]
    serializer_class = AdminUserListSerializer

    def get_queryset(self):
        return User.objects.filter(mentor=self.request.user, role=User.Role.USER).order_by('-date_joined')


class MentorStudentDetail360View(APIView):
    """Vue 360° d'un étudiant : réservée au mentor assigné (onglets Formations, Marché, Docs BP/BMC/Pitch, Mentorat)."""
    permission_classes = [permissions.IsAuthenticated, IsMentor]

    def get(self, request, pk):
        user = get_object_or_404(User, pk=pk, mentor=request.user, role=User.Role.USER)

        formations_data = []
        all_formations = Formation.objects.all()
        for f in all_formations:
            total_lessons = f.lessons.count()
            completed_lessons = UserProgress.objects.filter(
                user=user, lesson__formation=f, is_completed=True
            ).count()
            progress = (completed_lessons / total_lessons * 100) if total_lessons > 0 else 0
            formations_data.append({
                "id": f.id,
                "title": f.title,
                "progress": round(progress, 2),
                "is_validated": progress == 100,
            })

        study = MarketSurvey.objects.filter(entrepreneur=user).first()
        market_responses_count = study.responses.count() if study else 0
        market_stats = None
        market_responses = []
        if study:
            responses_qs = study.responses.all().order_by('-created_at')
            market_responses = SurveyResponseSerializer(responses_qs, many=True).data
            age_stats = study.responses.values('age_range').annotate(count=Count('id'))
            gender_stats = study.responses.values('gender').annotate(count=Count('id'))
            interest_stats = study.responses.values('interest_level').annotate(count=Count('id'))
            price_stats = study.responses.values('general_price_perception').annotate(count=Count('id'))
            location_stats = study.responses.values('location').annotate(count=Count('id'))
            market_stats = {
                "total_responses": market_responses_count,
                "required_responses": MARKET_REQUIRED_RESPONSES,
                "responses_needed": max(0, MARKET_REQUIRED_RESPONSES - market_responses_count),
                "is_unlocked": market_responses_count >= MARKET_REQUIRED_RESPONSES,
                "age_distribution": {item['age_range']: item['count'] for item in age_stats},
                "gender_distribution": {item['gender']: item['count'] for item in gender_stats},
                "interest_distribution": {item['interest_level']: item['count'] for item in interest_stats},
                "price_perception": {item['general_price_perception']: item['count'] for item in price_stats},
                "location_distribution": {item['location']: item['count'] for item in location_stats},
            }

        has_bp = BusinessPlan.objects.filter(entrepreneur=user).exists()
        bp_id = BusinessPlan.objects.filter(entrepreneur=user).values_list('id', flat=True).first() if has_bp else None
        has_bmc = BusinessModelCanvas.objects.filter(entrepreneur=user).exists()
        bmc_id = BusinessModelCanvas.objects.filter(entrepreneur=user).values_list('id', flat=True).first() if has_bmc else None
        has_pitch = PitchDeck.objects.filter(entrepreneur=user).exists()
        pitch_id = PitchDeck.objects.filter(entrepreneur=user).values_list('id', flat=True).first() if has_pitch else None

        return Response({
            "user": {
                "id": user.id,
                "first_name": user.first_name,
                "last_name": user.last_name,
                "email": user.email,
                "phone": user.phone,
                "date_joined": user.date_joined.strftime("%d/%m/%Y"),
                "is_verified": user.is_verified,
                "is_active": user.is_active,
                "role": user.role,
                "location": getattr(user, "location", None) or None,
            },
            "global_progress": round(sum(f['progress'] for f in formations_data) / len(all_formations), 2) if all_formations else 0,
            "formations": formations_data,
            "market_responses_count": market_responses_count,
            "market_required_responses": MARKET_REQUIRED_RESPONSES,
            "market_stats": market_stats,
            "market_responses": market_responses,
            "has_bp": has_bp,
            "bp_id": bp_id,
            "has_bmc": has_bmc,
            "bmc_id": bmc_id,
            "has_pitch": has_pitch,
            "pitch_id": pitch_id,
        })