import json
from datetime import date, timedelta, datetime
from dateutil.relativedelta import relativedelta

from django.utils import timezone
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required

from django.http import JsonResponse
from django.forms.models import model_to_dict
from django.db.models import Sum

from .models import Employe, Contrat, Affectation, Formation
from .forms import AffectationForm, ContratForm, FormationForm
from fonction_utilitaire import fonctions_utilitaire

@login_required
def index(request):
    """Vue d'index"""
    employes = Employe.objects.all().order_by('-user__date_joined')
    nombre_employes = Employe.objects.count()
    nombre_cps = Contrat.objects.filter(type_contrat='contrat_prestation').count()
    nombre_stage = Contrat.objects.filter(type_contrat='contrat_stage').count()
    date_limite = timezone.now().date() + timedelta(days=60)
    nombre_expirants = Contrat.objects.filter(
        date_fin__lte=date_limite,
        date_fin__gte=timezone.now().date(),
        statut='actif'
    ).count()

    return render(request, 'gestion_employe/index.html', {
        'employes': employes,
        'nombre_employes': nombre_employes,
        'nombre_cps': nombre_cps,
        'nombre_stage': nombre_stage,
        'nombre_expirants': nombre_expirants,
        'affectation_form': AffectationForm(),
        'contrat_form': ContratForm()
    })

@login_required
def liste_contrat_expirants(request):
    """ Liste des contrats proches """
    
    date_limite = timezone.now().date() + timedelta(days = fonctions_utilitaire.DUREE_FIN_CONTRAT)
    contats_expirants = [
        {
            'employe': f"{contrat.employe.user.first_name} {contrat.employe.user.last_name}",
            'type_contrat': dict(Contrat.TYPE_CONTRAT).get(contrat.type_contrat),
            'date_debut': contrat.date_debut,
            'date_fin': contrat.date_fin,
            'statut': contrat.statut,
            'fichier_contrat': contrat.fichier_contrat.url if contrat.fichier_contrat else ""
        }
        for contrat in
        Contrat.objects.filter(date_fin__lte=date_limite, date_fin__gte=timezone.now().date(), statut='actif')
    ]

    return JsonResponse(contats_expirants, safe=False)

@login_required
def affecter_employe_projet(request):
    """Vue pour affecter un employé à un projet avec vérification des contraintes d'affectation"""
    if request.method == 'POST':
        employe_id = request.POST.get('affecter_employe_id')
        try:
            employe = Employe.objects.get(id=employe_id)
        except Employe.DoesNotExist:
            messages.error(request, "L'employé spécifié n'existe pas.")
            return redirect('gestion_employe:index')
        
        form = AffectationForm(request.POST)
        if form.is_valid():
            projet = form.cleaned_data['projet']
            date_fin_affectation = form.cleaned_data['date_fin_daffectation'] 
            temps_nouveau = form.cleaned_data['pourcentage_temps_affectation']
            date_affectation = form.cleaned_data['date_affectation']

            if (date_fin_affectation and date_affectation):
                total_affectation = (
                    Affectation.objects.filter(employe=employe)
                    .aggregate(total_pourcentage_affectation = Sum('pourcentage_temps_affectation'))
                    ['total_pourcentage_affectation'] or 0
                )
                if (date_fin_affectation < date_affectation):
                    messages.warning(request, "La date de fin d'affectation ne peut pas être antérieure à la date de début.")
                    return redirect('gestion_employe:index')
                elif date_fin_affectation > projet.date_fin:
                    messages.warning(request, f"La date de fin de l'affectation ({date_fin_affectation}) ne peut pas dépasser la date de fin du projet ({projet.date_fin}).")
                    return redirect('gestion_employe:index')
                elif total_affectation + temps_nouveau > 100:
                    messages.warning(
                        request, 
                        f"Les pourcentages d'affectation de l'employé {employe.first_name} {employe.last_name} dépasse 100% sur les différents projets ({total_affectation + temps_nouveau}%)."
                    )
                    return redirect('gestion_employe:index')

            Affectation.objects.update_or_create(
                employe=employe,
                projet=projet,
                defaults={
                    'date_affectation': form.cleaned_data['date_affectation'],
                    'date_fin_daffectation': date_fin_affectation,
                    'role': form.cleaned_data['role'],
                    'pourcentage_temps_affectation': temps_nouveau,
                }
            )
            messages.success(request, f"L'employé {employe.user.first_name} {employe.user.last_name} a été affecté au projet {projet.nom_projet}.")

            fonctions_utilitaire.envoyer_mail(
                sujet = "Affectation à un projet",
                message = f"""Bonjour {employe.user.first_name} {employe.user.last_name}, vous avez été affecté au projet {projet.nom_projet.upper()} pour la période du {form.cleaned_data['date_affectation'].strftime('%d/%m/%Y')} au {date_fin_affectation.strftime('%d/%m/%Y')} en tant que {dict(Affectation.ROLE_CHOICES).get(form.cleaned_data['role'])}. 
Veuillez consulter votre profil pour plus de détails.""",
                destinataires = [employe.user.email]
            )
            return redirect('gestion_employe:index')
        else:
            messages.error(request, "Erreur : Formulaire non valide.")
            return redirect('gestion_employe:index')
    else:
        return redirect('gestion_employe:index')

@login_required
def mon_profil(request):
    """Vue pour afficher et modifier le profil de l'utilisateur connecté"""
    try:
        employe = Employe.objects.get(user__username=request.user)
    except Employe.DoesNotExist:
        messages.error(request, "Impossible d'acceder au menu 'Mon profil' car votre profil Utilisateur n'est lié à aucun profil Employé. Veuillez contacter l'Administrateur.")
        return redirect("gestion_conges:conge")
    
    contrats = Contrat.objects.filter(employe=employe, statut='actif').first()
    projets = Affectation.objects.filter(
        employe = employe, 
        date_fin_daffectation__gte = timezone.now().date()
    ).select_related('projet')

    return render(
        request, 
        'gestion_employe/monprofil.html', 
        {
            'employe': employe,
            'contrats': [{
                **model_to_dict(contrats),
                "type_contrat": dict(Contrat.TYPE_CONTRAT).get(contrats.type_contrat),
                "statut": dict(Contrat.STATUT_CONTRAT).get(contrats.statut),
                "fichier_contrat": contrats.fichier_contrat.url if contrats.fichier_contrat else "",
            } if contrats else []],
            'projets': [
                {
                    **model_to_dict(a.projet),
                    "date_affectation": a.date_affectation,
                    "date_fin_daffectation": a.date_fin_daffectation,
                    "role": dict(Affectation.ROLE_CHOICES).get(a.role),
                    "pourcentage_temps_affectation": a.pourcentage_temps_affectation
                } for a in projets
            ],
            "formation_form": FormationForm(),
            "expiration_contrat": contrats.nombre_jours_restant <= fonctions_utilitaire.DUREE_FIN_CONTRAT if contrats else False,
            "contrat_nb_jours_restant": contrats.nombre_jours_restant if contrats else None
        }
    )

@login_required
def modifier_mot_passe(request):
    """Vue pour permettre à un utilisateur de modifier son mot de passe et ses informations de profil"""
    user = User.objects.get(username=request.user)
    if request.method == "POST":
        ancien_mdp = request.POST["ancien-mdp"]
        nouveau_mdp = request.POST["nouveau-mdp"]
        confirmation_mdp = request.POST["confirmation-mdp"]

        if authenticate(request, username=request.user, password=ancien_mdp) is None:
            messages.error(request, "Ancien mot de passe incorrect.")
        elif nouveau_mdp != confirmation_mdp:
            messages.error(request, "Les deux nouveaux ne correspondent pas.")
        else:
            user.set_password(nouveau_mdp)
            user.save()
            messages.success(request, "Mot de passe modifié avec succès.")

    return redirect("gestion_employe:mon-profil")

def modifier_employer(request):
    """Vue pour permettre à un utilisateur de modifier les informations d'un employé"""
    try:
        employe = Employe.objects.get(user__username=request.user)
    except Employe.DoesNotExist:
        return JsonResponse({"message": "Employé non trouvé."})
    if request.method == "POST":
        data = json.loads(request.body)
        user = User.objects.get(username=request.user)
        user.last_name = data['nom']
        user.first_name = data['prenom']
        user.email = data['email']
        employe.telephone = data['telephone']
        employe.adresse = data['adresse']
        employe.sexe = data['sexe']
        if request.FILES.get("photo"):
            employe.photo = request.FILES["photo"]    
        if data['date_naissance']:
            difference = relativedelta(timezone.now().date(), datetime.strptime(data['date_naissance'], "%Y-%m-%d").date())
            if difference.years >= 18:
                employe.date_naissance = data['date_naissance']
            else:
                return JsonResponse({"message": "Veuillez entrez une date de naissance correcte."})
        employe.save()
        user.save()
        return JsonResponse({"message": "Profil mis à jour avec succès."})

def enregistrement_document(request):
    """Vue pour permettre à un utilisateur de télécharger et enregistrer des documents liés à son profil"""
    employe = Employe.objects.get(user=request.user)
    if request.method == "POST":
        if request.FILES.get("photo"): 
            employe.photo = request.FILES["photo"]
        if "cv" in request.FILES:
            employe.CV = request.FILES["cv"]
        if "diplome" in request.FILES: 
            employe.diplome = request.FILES["diplome"]
        if "rib" in request.FILES: 
            employe.rib = request.FILES["rib"]
        if "casier_judiciaire" in request.FILES:
            employe.casier_judiciaire = request.FILES["casier_judiciaire"]
        employe.save()
        messages.success(request, "Documents enregistrés avec succès.")

    return redirect("gestion_employe:mon-profil")

def suppression_contrat(request):
    """Vue pour permettre à un utilisateur de supprimer un contrat"""
    id_contrat = json.loads(request.body)['id']
    try:
        contrat = Contrat.objects.get(numero_contrat = id_contrat)
    except Contrat.DoesNotExist:
        messages.error(request, "Contrat non trouvé.")
        return JsonResponse({"message": "Contrat non trouvé."}, status=404)
    
    contrat.delete()
    return JsonResponse({"message": "Contrat supprimé avec succès."})

def suppression_affectation(request):
    """Vue pour permettre à un utilisateur de supprimer une affectation"""
    id_affectation = json.loads(request.body)['id']
    try:
        affectation = Affectation.objects.get(id=id_affectation)
    except Affectation.DoesNotExist:
        return JsonResponse({"message": "Affectation non trouvée."}, status=404)
    
    affectation.delete()
    return JsonResponse({"message": "Affectation supprimée avec succès."})

def creation_contrat(request):
    """Créer un contrat pour un employé (avec contrôle d'existence de contrat actif)"""

    try:
        employe = Employe.objects.get(id=request.POST.get('employe_id'))
    except Employe.DoesNotExist:
        messages.error(request, "Employé non trouvé.")
        return redirect('employe-index')
    contrat_actif = Contrat.objects.filter(
        employe=employe,
        date_fin__gte=date.today()
    ).exists()

    if request.method == "POST":
        if contrat_actif:
            messages.error(
                request,
                "Impossible de créer un contrat : cet employé a déjà un contrat actif."
            )
            return redirect('gestion_employe:index')
        form = ContratForm(request.POST, request.FILES)
        if form.is_valid():
            contrat = form.save(commit=False)
            contrat.employe = employe
            contrat.save()
            messages.success(request, "Contrat créé avec succès.")
            return redirect('gestion_employe:index')
        messages.error(request, "Formulaire non valide")

    else:
        form = ContratForm(initial={'employe': employe})

    return render(request, 'gestion_employe/index.html', {
        'contrat_form': form
    })

@login_required
def enregistrer_detail_employe(request):
    """Vue pour permettre à un utilisateur de modifier les détails d'un employé via une requête AJAX"""
    if request.method == "POST":
        data = json.loads(request.body)
        try:
            employe = Employe.objects.get(id=data['id'])
        except Employe.DoesNotExist:
            return JsonResponse({"error": "Employé non trouvé."}, status=404)

        employe.fonction = data['fonction']
        employe.date_embauche = data['date_embauche']
        employe.matricule = data['matricule']
        employe.save()
        return JsonResponse({"message": "Détails de l'employé mis à jour avec succès."})
    else:
        return JsonResponse({"message": "Méthode non autorisée."}, status=405)
    
@login_required
def liste_employe(request):
    """ Vue pour retourner la liste de tous les employés """
    employes = Employe.objects.exclude(user__first_name = '', user__last_name = '')

    data = []
    for employe in employes:
        if employe.user.first_name != ' ' and employe.user.last_name != ' ':
            projets =  [
                ", ".join([
                    a.projet.nom_projet for a in Affectation.objects.filter(
                        employe=employe, 
                        date_fin_daffectation__gte=timezone.now().date()
                    )
                ])
            ] 
            formations = [
                {
                    "titre": formation.titre,
                    "organisme": formation.organisme,
                    "description": formation.description,
                    "date_obtention": formation.date_obtention,
                    "date_fin": formation.date_fin,
                    "certificat": formation.certificat.url if formation.certificat else "",
                } for formation in Formation.objects.filter(employe=employe)
            ]

            contrats = [
                {
                    "numero_contrat": contrat.numero_contrat,
                    "type_contrat": contrat.type_contrat,
                    "date_debut": contrat.date_debut,
                    "date_fin": contrat.date_fin,
                    "salaire_mensuel": contrat.salaire_mensuel,
                    "statut": contrat.statut,
                    "fichier_contrat": contrat.fichier_contrat.url if contrat.fichier_contrat else "",
                } for contrat in Contrat.objects.filter(employe=employe, statut='actif')
            ]

            affectations = [
                {**model_to_dict(affectation), "projet": affectation.projet.nom_projet}
                for affectation in Affectation.objects.filter(
                    employe=employe, 
                    date_fin_daffectation__gte=timezone.now().date()
                )
            ]

            data.append(
                {
                    "id": employe.id,
                    "employe": f"{employe.user.first_name} {employe.user.last_name}", 
                    "matricule": employe.matricule,
                    "email": employe.user.email,
                    "formations": formations,
                    "affectations": affectations,
                    "projet": projets,
                    "contrats": contrats,
                    "departement": employe.departement.nom if employe.departement else "",
                    "fonction": employe.fonction,
                    "date_embauche": employe.date_embauche,
                    "adresse": employe.adresse,   
                    "telephone": employe.telephone,
                    "sexe": employe.sexe,
                    "CV": employe.CV.url if employe.CV else "",
                    "diplome": employe.diplome.url if employe.diplome else "",
                    "rib": employe.rib.url if employe.rib else "",
                    "photo": employe.photo.url if employe.photo else "",
                    "casier_judiciaire": employe.casier_judiciaire.url if employe.casier_judiciaire else "",
                    "date_naissance": employe.date_naissance,
                }
            )
    return JsonResponse({'success': True, 'data': data}, safe=False)

@login_required
def ajouter_formation(request):
    """Vue pour permettre à un employé d'ajouter une formation à son profil"""
    employe = Employe.objects.get(user__username=request.user)
    if request.method == "POST":
        formation = FormationForm(request.POST, request.FILES)
        if formation.is_valid():
            ma_formation = formation.save(commit=False)
            ma_formation.employe = employe
            ma_formation.save()
            messages.success(request, "Formation ajoutée avec succès ")
            return redirect("gestion_employe:mon-profil")
        messages.error(request, "Formulaire non valide. Veuillez vérifier les informations saisies.")
    return redirect("gestion_employe:mon-profil")

def liste_formation(request):
    formations = Formation.objects.filter(employe__user__username=request.user).order_by("-date_obtention")
    return JsonResponse([
        {
            **model_to_dict(formation), 
            "certificat": formation.certificat.url if formation.certificat else ""
        }
        for formation in formations
    ], safe=False)

@login_required
def modifier_formation(request, id_formation):
    """Vue pour permettre à un employé de modifier une formation de son profil"""
    try:
        formation = Formation.objects.get(id=id_formation, employes=request.user)
    except Formation.DoesNotExist:
        messages.error(request, "Formation non trouvée.")
        return redirect("mes_formations")

    if request.method == "POST":
        formation = FormationForm(request.POST, request.FILES, instance=formation)
        if formation.is_valid():
            messages.success(request, "Formation mise à jour ")

            formation.save()
        messages.error(request, "Formulaire non valide. Veuillez vérifier les informations saisies.")
    return redirect("mes_formations")

@login_required
def supprimer_formation(request, id_formation):
    """Vue pour permettre à un employé de supprimer une formation de son profil"""
    try:
        formation = Formation.objects.get(id=id_formation, employes=request.user)
    except Formation.DoesNotExist:
        messages.error(request, "Formation non trouvée.")
        return redirect("mes_formations")

    if request.method == "POST":
        formation.delete()
        messages.success(request, "Formation supprimée ")
    return redirect("mes_formations")
