from datetime import date
from decimal import Decimal, InvalidOperation
from django.http import JsonResponse
from django.shortcuts import redirect, render
from django.utils import timezone
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Sum
from django.forms.models import model_to_dict
from gestion_employe.forms import AffectationForm
from gestion_employe.models import Affectation, Employe
from gestion_projet.forms import ProjetForm
from gestion_projet.models import Projet
from .models import (
    DocumentProjet, 
    Bailleur, 
    FinancementProjet, 
    ActiviteProjet,
    LivrablesLivres,
)
from .forms import (
    ActiviteProjetForm,
    DocumentProjetForm,
    FinancementProjetFrom,
    BailleurForm,
    LivrablesLivresForm
)

def liste_projet(request):
    """ Vue pour retourner la liste de tous les projet """
    projets = Projet.objects.all().order_by('-created_at')

    data = []
    for p in projets:
        financement = FinancementProjet.objects.filter(projet=p).select_related('bailleur')
        data.append({
            "id_projet": p.id_projet,
            "nom_projet": p.nom_projet,
            "date_debut": p.date_debut,
            "date_fin": p.date_fin,
            "numero_convention": p.numero_convention,
            "description": p.description,
            "type_projet": p.type_projet,
            "budget": p.budget,
            "budget_RH": p.budget_RH,
            "created_at": p.created_at,
            "statut": p.statut,
            "avancement": p.avancement,
            "domaine_recherche": [d.nom for d in p.domaine_recherche.all()],
            "source_financement": [f.bailleur.nom_organisme for f in financement],
        })

    return JsonResponse(data, safe=False)

def liste_employes_affectes(request, projet_id):
    """ Vue pour retourner la liste des employés affectés à un projet spécifique """
    employes = Employe.objects.filter(affectation__projet_id=projet_id).distinct()
    data = []
    for employe in employes:
        data.append({
            "employe": f"{employe.user.first_name} {employe.user.last_name}",
            "pourcentage_affectation": Affectation.objects.get(employe=employe, projet__id_projet=projet_id).pourcentage_temps_affectation
        })
    return JsonResponse(data, safe=False)

def liste_bailleurs(request, projet_id):
    """ Vue pour retourner la liste des bailleurs associés à un projet spécifique """
    bailleurs = FinancementProjet.objects.filter(projet_id=projet_id).select_related('bailleur')
    data = []
    for b in bailleurs:
        data.append({
            "bailleur": b.bailleur.nom_organisme,
            "pourcentage_financement": b.pourcentage
        })

    return JsonResponse(data, safe=False)

@login_required
def index(request):
    projets = Projet.objects.all().order_by('-created_at')
    nombre_personnel = Affectation.objects.values('employe_id').distinct().count()
    budget_total = sum([projet.budget for projet in projets if projet.budget or 0])
    context = {
        'form': AffectationForm(),
        'form_ajout_financement': FinancementProjetFrom(),
        'form_ajout_bailleur': BailleurForm(),
        'bailleurs': Bailleur.objects.all(),
        'nombre_personnel': nombre_personnel,
        'budget_total': budget_total,
        'formulaire_creation_projet': ProjetForm(),
        'projet_en_cours': Projet.objects.filter(date_fin__gte=date.today()).count(),
    }

    return render(request, 'gestion_projet/index.html', context)

@login_required
def creation_projet(request):
    """Vue pour créer un nouveau projet via un formulaire"""
    formulaire_creation_projet =  ProjetForm()
    if request.method == "POST":
        form = ProjetForm(request.POST)
        
        if form.is_valid():
            form.save()
            messages.success(request, "Projet créé avec succès.")
        else:
            messages.error(request, "Le formulaire transmis est invalide.") 
    else:
        form = ProjetForm()
    return render(
        request, 
        "gestion_projet/creation_projet.html", 
        {
            "formulaire_creation_projet": formulaire_creation_projet
        }
    )

@login_required
def mises_a_jour_projet(request):
    """ Vue de mises à jour des informations du projet """
    if request.method == "POST":
        try:
            projet = Projet.objects.get(id_projet = request.POST["id_projet"])
        except Projet.DoesNotExist:
            messages.error(request, "Ce projet n'existe pas.")
        else:
            projet_form = ProjetForm(request.POST, instance=projet)
            if projet_form.is_valid():
                projet_form.save()
                messages.success(request, f"Le projet d'identifiant {request.POST['id_projet']} a été mis à jour avec succès.")
            else:
                messages.error(request, f"Les informations de modification transmises pour la modification du projet {request.POST['id_projet']} ne sont pas valides.")
    else:
        messages.error(request, "La méthode de transmission des données n'est pas valide.")
    return redirect('gestion_projet:index')

@login_required
def creation_bailleur(request):
    form = BailleurForm(request.POST)
    if request.method == 'POST':
        if form.is_valid():
            form.save()
            return JsonResponse({'success': True})
    return JsonResponse({'success': False})

@login_required
def liste_bailleur(request):
    """ Vue pour retourner la liste de tous les bailleurs """
    bailleurs = Bailleur.objects.all().order_by('-id')
    data = []
    for b in bailleurs:
        data.append({
        "id": b.id,
        "nom_organisme": b.nom_organisme,
        "contact": b.contact,
        "email": b.email,
        "pays": b.pays,
        })
    return JsonResponse(data, safe=False)


@login_required
def ajouter_financement_projet(request):
    """Ajoute un financement à un projet en vérifiant que le total ne dépasse pas 100%"""

    if request.method == 'POST':
        pourcentage_recuperer = request.POST.get('pourcentage')
        bailleur_id = request.POST.get('bailleur')
        projet_id = request.POST.get('projet')
        try:
            projet = Projet.objects.get(id_projet=projet_id)
        except Projet.DoesNotExist:
            return JsonResponse({'success': False, 'message': "Le projet spécifié n’existe pas."})

        try:
            pourcentage_nouveau = Decimal(pourcentage_recuperer) if pourcentage_recuperer else Decimal(0)
        except (InvalidOperation, TypeError):
            return JsonResponse({'success': False, 'message': "Le pourcentage saisi n’est pas valide."})
        
        financement_total_actuel = sum(financement.pourcentage for financement in FinancementProjet.objects.filter(projet=projet))
        if financement_total_actuel + pourcentage_nouveau > 100:
            return JsonResponse({'success': False, 'message': "Le total des financements dépasse 100%."})

        if bailleur_id:
            FinancementProjet.objects.create(
                projet=projet,
                bailleur_id=bailleur_id,
                pourcentage=pourcentage_nouveau
            )
            return JsonResponse({'success': True, 'message': "Financement ajouté avec succès."})
        else:
            return JsonResponse({'success': False, 'message': "Aucun bailleur sélectionné."})
    return JsonResponse({'success': False, 'message': "Requête invalide."})

@login_required
def modification_projet(request, projet_id):
    """Vue pour éditer un projet existant via un formulaire pré-rempli"""
    try:
        projet = Projet.objects.get(id=projet_id)
    except Projet.DoesNotExist:
        messages.error(request, "Le projet spécifié n’existe pas.")
        return redirect('projet-index')

    if request.method == "POST":
        form = ProjetForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Le projet a été modifié avec succès.")
            return redirect('projet-index')
        messages.error(request, "Erreur lors de la modification du projet.")
    form = ProjetForm(instance=projet)
    return render(request, 'gestion_projet/projet-edit.html', {'form': form, 'projets': projet})

@login_required
def suppression_projet(request, id):
    """Vue pour supprimer un projet spécifique après confirmation de l'utilisateur"""
    try:
        projet = Projet.objects.get(id=id)
    except Projet.DoesNotExist:
        messages.error(request, "Le projet spécifié n’existe pas.")
        return redirect('projet-index')
    
    if request.method == "POST":
        projet.delete()
        messages.success(request, "Le projet a été supprimé avec succès.")
    return redirect('projet-index')

@login_required
def affecter_employe_projet(request, projet_id):
    """Vue pour affecter un employé à un projet avec vérification des contraintes d'affectation"""
    try:
        projet = Projet.objects.get(id=projet_id)
    except Projet.DoesNotExist:
        messages.error(request, "Le projet spécifié n’existe pas.")
        return redirect('projet-index')
    
    if request.method == 'POST':
        form = AffectationForm(request.POST)
        if form.is_valid():
            employe = Employe.objects.get(id=form.cleaned_data['employe'].id)
            date_fin_affectation = form.cleaned_data['date_fin_daffectation'] 
            temps_nouveau = form.cleaned_data['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('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('projet-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('projet-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('projet-index')

            Affectation.objects.update_or_create(
                projet=projet,
                employe=employe,
                defaults={
                    'date_affectation': form.cleaned_data['date_affectation'],
                    'date_fin_daffectation': date_fin_affectation,
                    'role': form.cleaned_data['role'],
                    'temps_affectation': temps_nouveau
                }
            )
        form = AffectationForm(initial={'projet': projet})
        messages.error(request, "Erreur : Formulaire non valide.")
    return redirect('projet-index')

def modifier_financement_projet(request, financement_id):
    try:
        financement = FinancementProjet.objects.get(id=financement_id)
    except FinancementProjet.DoesNotExist:
        messages.error(request, "Le financement spécifié n’existe pas.")
        return redirect('projet-index')
    projet = financement.projet
    if request.method == 'POST':
        try:
            nouveau_pourcentage = Decimal(request.POST.get('pourcentage', '0'))
        except InvalidOperation:
            messages.error(request, "Le pourcentage saisi est invalide.")
            return redirect('projet-index')
        pourcentage_total_financement = (
            FinancementProjet.objects.filter(projet=projet)
            .exclude(id=financement.id)
            .aggregate(total_financement=Sum('pourcentage'))['pourcentage_total_financement'] or 0
        )
        if pourcentage_total_financement + nouveau_pourcentage > 100:
            messages.error(request, f"Le total des financements dépasse 100% ({pourcentage_total_financement + nouveau_pourcentage}%).")
            return redirect('projet-index')
        financement.pourcentage = nouveau_pourcentage
        financement.save()
        messages.success(request, "Financement modifié avec succès.")
    return redirect('projet-index')

@login_required
def activites_projet(request):
    try:
        employe = Employe.objects.get(user=request.user)
    except Employe.DoesNotExist:
        messages.error(request, "Impossible d'accéder au menu 'Suivi des activités' car votre profil Utilisateur n'est lié à aucun profil Employe. Veuillez contacter l'administrateur.")
        return redirect("gestion_conges:conge")
    
    try:
        Affectation.objects.get(employe=employe, date_fin_daffectation__gte = timezone.now().date(), role='chef_projet')
    except Affectation.DoesNotExist :
        messages.error(request, "Seuls les chefs de projet ont accès à l'onglet 'Suivi des Activités'")
        return redirect("gestion_conges:conge")
    
    projet = Affectation.objects.filter(employe=employe, role='chef_projet', date_fin_daffectation__gte=timezone.now().date()).select_related('projet').first()
    if projet :
        context = {
            **model_to_dict(projet), 
            "nom_projet": projet.projet.nom_projet,
            "budget_total": projet.projet.budget,
            "budget_RH": projet.projet.budget_RH,
            "form_ajout_activite": ActiviteProjetForm(),
            "form_ajout_document": DocumentProjetForm(),
            "form_ajout_livrable": LivrablesLivresForm(),
        }
    else :
        context = {
            "form_ajout_activite": ActiviteProjetForm(),
            "form_ajout_document": DocumentProjetForm(),
            "form_ajout_livrable": LivrablesLivresForm(),
        }
    return render(request, 'gestion_projet/suivi_activite.html', context)


@login_required
def ajouter_activite_projet(request):
    """Vue pour ajouter une activité à un projet spécifique via un formulaire"""
    employe = Employe.objects.get(user=request.user)
    projet = Affectation.objects.filter(employe=employe, role='chef_projet', date_fin_daffectation__gte=timezone.now().date()).select_related('projet').first()

    if request.method == "POST":
        form = ActiviteProjetForm(request.POST)
        if form.is_valid():
            activite = form.save(commit=False)
            activite.projet = projet.projet
            activite.budget_depense = request.POST["budget_prevu"]
            activite.save()
            messages.success(request, "Activité ajoutée avec succès !")
        else:
            messages.error(request, "Erreur : vérifiez les informations saisies.")
    return redirect('gestion_projet:activites-projet')

@login_required
def liste_activites_projet(request):
    """Vue pour retourner la liste des activités d'un projet spécifique"""
    employe = Employe.objects.get(user=request.user)
    projet = Affectation.objects.filter(employe=employe, role='chef_projet', date_fin_daffectation__gte=timezone.now().date()).select_related('projet').first()
    if projet:
        activites = ActiviteProjet.objects.filter(projet_id=projet.projet.id_projet).order_by('-date_debut')
    else:
        activites = []
    data = []
    for a in activites:
        data.append({
            "id": a.id,
            "titre": a.titre,
            "date_debut": a.date_debut,
            "date_fin": a.date_fin,
            "statut": a.statut,
            "budget_prevu": a.budget_prevu,
            "budget_depense": a.budget_depense,
            "motif_changement_budget": a.motif_changement_budget,
            "besoin_ressource_materielle": a.besoin_ressource_materielle,
            "description": a.description,
        })
    return JsonResponse(data, safe=False)

@login_required
def liste_livrables_activite(request, activite_id):
    """Vue pour retourner la liste des livrables attendus d'une activité spécifique"""
    livrables = LivrablesLivres.objects.filter(activite__id=activite_id)
    data = []
    for livrable in livrables:
        print(livrable.fichier.url)
        data.append({
            "titre": livrable.nom,
            "lien": livrable.fichier.url if livrable.fichier else "",
        })
    return JsonResponse(data, safe=False)

@login_required
def mises_a_jour_depense_activite(request):
    """Vue pour retourner la liste des activités d'un projet spécifique avec leurs dépenses mises à jour"""
    if request.method == "POST":
        activite_id = request.POST.get("id_activite")
        budget_depense = request.POST.get("budget_depense")
        motif = request.POST.get("motif", "").strip()
        try:
            activite = ActiviteProjet.objects.get(id=activite_id)
            activite.budget_depense = Decimal(budget_depense)
            if Decimal(budget_depense) != activite.budget_prevu:
                activite.motif_changement_budget = motif
            else:
                activite.motif_changement_budget = ""
            activite.save()
            messages.success(request, f"Dépenses mises à jour pour l’activité '{activite.titre}'.")
        except (ActiviteProjet.DoesNotExist, InvalidOperation):
            messages.error(request, "Erreur lors de la mise à jour des dépenses.")
    return redirect("gestion_projet:activites-projet")

@login_required
def ajouter_livrables_projet(request):
    """Vue pour ajouter un livrable à une activité de projet spécifique via un formulaire"""

    if request.method == "POST":
        form = LivrablesLivresForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            messages.success(request, "Livrable ajouté avec succès !")
        else:
            messages.error(request, "Erreur : vérifiez les informations saisies.")
    return redirect('gestion_projet:activites-projet')

@login_required
def ajouter_document_projet(request):
    """Ajoute un document à un projet"""
    employe = Employe.objects.get(user=request.user)
    projet = Affectation.objects.filter(employe=employe, role='chef_projet', date_fin_daffectation__gte=timezone.now().date()).select_related('projet').first()
    if request.method == "POST":
        form = DocumentProjetForm(request.POST, request.FILES)
        if form.is_valid():
            document = form.save(commit=False)
            document.projet = projet.projet
            document.save()
            messages.success(request, "Document ajouté avec succès !")
        else:
            messages.error(request, "Erreur : le document n’a pas pu être enregistré.")
    return redirect('gestion_projet:activites-projet') 

def liste_documents_projet(request):
    employe = Employe.objects.get(user=request.user)
    projet = Affectation.objects.filter(employe=employe, role='chef_projet', date_fin_daffectation__gte=timezone.now().date()).select_related('projet').first()
    if projet:
        documents = DocumentProjet.objects.filter(projet__id_projet=projet.projet.id_projet)
    else:
        documents = []
    data = []
    for d in documents:
        data.append({
            "nom_document": d.nom_document,
            "numero": d.numero,
            "date_validite": d.date_validite,
            "lien_document": d.fichier.url if d.fichier else "",
        })
    return JsonResponse(data, safe=False)

def modifier_activite_projet(request, id):
    """Vue pour modifier une activité de projet spécifique via un formulaire pré-rempli"""
    try:
        activite = ActiviteProjet.objects.get(id=id)
    except ActiviteProjet.DoesNotExist:
        messages.error(request, "L'activité spécifiée n’existe pas.")
        return redirect('activites-projet')
    if request.method == 'POST':
        form = ActiviteProjetForm(request.POST, instance=activite)
        if form.is_valid():
            activite.besoin_ressource_materielle = bool(request.POST.get("besoin_ressource_materielle"))
            form.save()
            messages.success(request, f"Activité « {activite.titre} » modifiée avec succès.")
        else:
            messages.error(request, "Erreur lors de la modification de l'activité.")
        return redirect('activites-projet')

    form = ActiviteProjetForm(instance=activite)
    return render(
        request, 
        'gestion_projet/activite.html', {
            'form': form,
            'activite': activite,
        }
    )

def annuler_activite_projet(request):
    """Vue pour annuler une activité de projet spécifique après confirmation de l'utilisateur"""
    print(request.POST)
    if request.method != "POST":
        messages.error(request, "Requête invalide.")
        return redirect('gestion_projet:activites-projet')
    try:
        activite = ActiviteProjet.objects.get(id=request.POST.get('id_activite'))
    except ActiviteProjet.DoesNotExist:
        messages.error(request, "L'activité spécifiée n’existe pas.")
        return redirect('gestion_projet:activites-projet')
    if request.method == "POST":
        activite.annuler = True
        activite.motif_annulation = request.POST.get("motif_annulation", "").strip()
        activite.save()
        messages.success(request, f"L'activité '{activite.titre}' a été annulée avec succès.")
    return redirect('gestion_projet:activites-projet')