from django.shortcuts import render
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
import stripe
import os
from datetime import datetime, timedelta
from django.utils import timezone
from user.models import User
from config.stripe_const import STRIPE_WEBHOOK_SECRET, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET_CHECKOUT_COMPLETED, STRIPE_WEBHOOK_SECRET_SUBSCRIPTION_DELETED, products
from rest_framework.decorators import authentication_classes, permission_classes, api_view
from config.authentication import JWTAuthenticationWithUserCheck, IsAuthenticated
from utils.manage_error import write_error_log
import json

stripe.api_key = STRIPE_SECRET_KEY

## Fonction de modification de user en cas de changement d'abonnement
def modify_user_abonnement(user, new_abonnement, abonnement_id = None, pack_id = None):
    if "is_abonnement_active" in new_abonnement:
        user.is_abonnement_active = new_abonnement["is_abonnement_active"]
    
    if "abonnement_type" in new_abonnement:
        user.abonnement_type = new_abonnement["abonnement_type"]
    
    if "abonnement_end_date" in new_abonnement:
        print(f"[modify_user_abonnement] Abonnement end date: {new_abonnement['abonnement_end_date']}")
        user.abonnement_end_date = new_abonnement["abonnement_end_date"]
    
    if "tokens_reset_date" in new_abonnement: # On prend la date la plus éloignée entre les deux abonnements
        if new_abonnement["tokens_reset_date"] > user.tokens_reset_date:
            user.tokens_reset_date = new_abonnement["tokens_reset_date"]
    
    if "tokens_limit" in new_abonnement: # On ajoute les nouveaux tokens à ceux déjà présents
        user.tokens_limit = user.tokens_limit + new_abonnement["tokens_limit"]
    
    if "new_pack" in new_abonnement:
        print("[modify_user_abonnement] Ajout d'un nouveau pack")
        new_pack = new_abonnement["new_pack"]
        if pack_id:
            print(f"[modify_user_abonnement] Pack ID fourni: {pack_id}")
            new_pack["id"] = pack_id
        print(f"[modify_user_abonnement] Ajout du pack: {new_pack}")
        user.packs.append(new_pack)
        
    if abonnement_id:
        user.subscription_id = abonnement_id

    user.save()

## Fonction de modification de user lors de la fin d'abonnement chaque mois
def recharge_user_abonnement(user, abonnement):
    if "tokens_limit" in abonnement:
        user.tokens_limit = user.tokens_limit + abonnement["tokens_limit"]
    if "abonnement_end_date" in abonnement:
        user.abonnement_end_date = abonnement["abonnement_end_date"]
    if "tokens_reset_date" in abonnement:
        user.tokens_reset_date = abonnement["tokens_reset_date"]
    user.save()

def is_same_product(user, product_type):
    product_type_mapping = {
        "free": "FR",
        "basique": "BS",
        "premium": "PR",
        "revisions": "RE"
    }
    if user.abonnement_type == product_type_mapping[product_type]:
        return True
    return False

def abonnements():
    return {
        "free": {
            "is_abonnement_active": False,
            "abonnement_type": "FR",
            "tokens_limit": 10000
        },
        "basique": {
            "is_abonnement_active": True,
            "abonnement_type": "BS",
            "tokens_limit": 220000,
            "abonnement_end_date": timezone.now() + timedelta(days=31),
            "tokens_reset_date": timezone.now() + timedelta(days=31)
        },
        "revisions": {
            "new_pack": {
                "name": "revisions",
                "end_date": (timezone.now() + timedelta(days=4, hours=2)).isoformat()
            }
        },
        "premium": {
            "is_abonnement_active": True,
            "abonnement_type": "PR",
            "tokens_limit": 5000000,
            "abonnement_end_date": timezone.now() + timedelta(days=31),
            "tokens_reset_date": timezone.now() + timedelta(days=31)
        }
    }
    

def get_user_from_session(session):
    """Fonction utilitaire pour récupérer l'utilisateur à partir d'une session Stripe"""
    print("[get_user_from_session] Début de la recherche de l'utilisateur")
    user = None
    
    # 1. Essai avec client_reference_id
    client_reference_id = session.get('client_reference_id')
    if client_reference_id:
        print(f"[get_user_from_session] Tentative avec client_reference_id: {client_reference_id}")
        try:
            user = User.objects.get(publicIdentifier=client_reference_id)
            print("[get_user_from_session] Utilisateur trouvé avec client_reference_id")
        except User.DoesNotExist:
            print("[get_user_from_session] Utilisateur non trouvé avec client_reference_id")
            pass
    
    # 2. Si échec, essai avec customer_email
    if not user and session.get('customer_email'):
        print(f"[get_user_from_session] Tentative avec customer_email: {session.get('customer_email')}")
        try:
            user = User.objects.get(email=session.get('customer_email'))
            print("[get_user_from_session] Utilisateur trouvé avec customer_email")
        except User.DoesNotExist:
            print("[get_user_from_session] Utilisateur non trouvé avec customer_email")
            pass
    
    # 3. Si échec, essai avec customer_details.email
    if not user and session.get('customer_details', {}).get('email'):
        print(f"[get_user_from_session] Tentative avec customer_details.email: {session['customer_details']['email']}")
        try:
            user = User.objects.get(email=session['customer_details']['email'])
            print("[get_user_from_session] Utilisateur trouvé avec customer_details.email")
        except User.DoesNotExist:
            print("[get_user_from_session] Utilisateur non trouvé avec customer_details.email")
            pass
    
    # 4. Si échec, récupérer le customer_id dans la base de données si il existe déjà
    customer_id = session.get('customer')
    if customer_id and not user:
        print(f"[get_user_from_session] Tentative avec customer_id: {customer_id}")
        try:
            user = User.objects.get(stripe_id=customer_id)
            print("[get_user_from_session] Utilisateur trouvé avec customer_id")
        except User.DoesNotExist:
            print("[get_user_from_session] Utilisateur non trouvé avec customer_id")
            pass
    
    # 5. Enregistrer le customer_id dans la base de données si il n'existe pas déjà
    if user and customer_id: # and not user.stripe_id
        print("[get_user_from_session] Mise à jour du stripe_id de l'utilisateur")
        user.stripe_id = customer_id
        user.save()
    
    print(f"[get_user_from_session] Fin de la recherche. Utilisateur trouvé: {user is not None}")
    return user


def get_line_items(session):
    """
    Récupère les line items d'une session, d'abord dans la requête puis via l'API Stripe si nécessaire
    """
    # Essai de récupération depuis la requête
    if 'items' in session:
        items = session.get('items', {}).get('data', [])
        if items and isinstance(items, list):
            print("[get_line_items] Line items trouvés dans la requête : ")
            return items

    # Si non trouvé dans la requête, appel à l'API Stripe
    try:
        items = stripe.checkout.Session.list_line_items(session['id'])
        print("[get_line_items] Line items trouvés via l'API Stripe")
        return items.data if items and items.data else []
    except Exception as e:
        print(f"Erreur lors de la récupération des line items : {str(e)}")
        return []


def verify_stripe_signature(payload, sig_header, STRIPE_WEBHOOK_SECRET):
    """Fonction utilitaire pour vérifier la signature Stripe"""
    
    # STRIPE_WEBHOOK_SECRET = "whsec_DKejVDvKv0RukNv2277eoNf4wZWizbgm" # A rebasculer dans un .env plus tard éventuellement
    try:
        return stripe.Webhook.construct_event(
            payload, sig_header, STRIPE_WEBHOOK_SECRET
        )
    except ValueError as e:
        print(f"Erreur lors de la vérification de la signature ou signature invalide : {str(e)}")
        return None
    except stripe.error.SignatureVerificationError as e:
        print(f"Signature invalide : {str(e)}")
        return None


@csrf_exempt
@require_POST
def stripe_webhook_checkout_completed(request):
    
    print("[stripe_webhook_checkout_completed] Début du traitement du webhook")
    payload = request.body
    sig_header = request.META['HTTP_STRIPE_SIGNATURE']
    
    try:
        print("[stripe_webhook_checkout_completed] Vérification de la signature")
        event = verify_stripe_signature(payload, sig_header, STRIPE_WEBHOOK_SECRET_CHECKOUT_COMPLETED)
        if not event:
            print("[stripe_webhook_checkout_completed] Erreur de vérification de la signature")
            return HttpResponse(status=401, body="Erreur de vérification de la signature")
        
        print(event)
        if event['type'] == 'checkout.session.completed' or event['type'] == 'customer.subscription.updated':
            session = event['data']['object']
                    
            print("[stripe_webhook_checkout_completed] Session reçue : ", session)
            
            # Vérification du statut du paiement
            if session.get('payment_status') != 'paid':
                print("[stripe_webhook_checkout_completed] Paiement non complété")
                return HttpResponse("Paiement non complété", status=400)

            # Récupération des détails de la ligne d'achat
            print("[stripe_webhook_checkout_completed] Récupération des line items")
            items = get_line_items(session)
            
            if len(items) == 0:
                print("[stripe_webhook_checkout_completed] Aucun produit trouvé")
                return HttpResponse("Aucun produit trouvé", status=400)
                
            # Récupération des abonnements actifs
            print("[stripe_webhook_checkout_completed] Récupération des abonnements actifs")
            active_subscriptions = stripe.Subscription.list(
                customer=session.get('customer'),
                status='active',
                limit=100
            )

            print(f"[stripe_webhook_checkout_completed] Abonnements actifs: {active_subscriptions}")
            
            # Annulation des anciens abonnements
            for subscription in active_subscriptions['data']:
                if subscription['id'] != session.get('subscription'):
                    print(f"[stripe_webhook_checkout_completed] Annulation de l'abonnement {subscription['id']}")
                    try:
                        stripe.Subscription.delete(subscription['id'])
                    except Exception as e:
                        print(f"[stripe_webhook_checkout_completed] Erreur lors de l'annulation de l'abonnement {subscription['id']}: {str(e)}")
            
            try:
                # Récupération du premier item (dans votre cas, il n'y en a qu'un)
                item = items[0] # items est déjà une liste
                product_id = item.price.product
                print(f"[stripe_webhook_checkout_completed] Product ID: {product_id}")

                if product_id not in products:
                    print("[stripe_webhook_checkout_completed] Produit non reconnu")
                    return HttpResponse("Produit non reconnu", status=400)
                
                product_type = products[product_id]
                print(f"[stripe_webhook_checkout_completed] Type de produit: {product_type}")
                
                # Récupération de l'utilisateur via la fonction utilitaire
                print("[stripe_webhook_checkout_completed] Récupération de l'utilisateur")
                user = get_user_from_session(session)
                print("user : ", user.email, user.abonnement_type, user.abonnement_end_date, user.tokens_reset_date)
                if not user:
                    print("[stripe_webhook_checkout_completed] Utilisateur non trouvé")
                    return HttpResponse("Utilisateur non trouvé", status=404)
                
                # On modifie user avec le nouvel abonnement
                new_abonnement = abonnements()[product_type]
                print(f"[stripe_webhook_checkout_completed] Modification de l'abonnement pour: {new_abonnement}")
                print(f"[stripe_webhook_checkout_completed] Product type: {product_type}")
                if product_type in ["free", "basique", "premium"]:
                    modify_user_abonnement(user, new_abonnement, item.get('subscription'))
                elif product_type in ["revisions"]:
                    modify_user_abonnement(user, new_abonnement)

            except Exception as e:
                print(f"[stripe_webhook_checkout_completed] Erreur lors du traitement du produit : {str(e)}")
                return HttpResponse("Erreur lors du traitement", status=400)
            
            print("[stripe_webhook_checkout_completed] Traitement terminé avec succès")
            return HttpResponse(status=200)

        return HttpResponse(status=400, body="Type d'événement non supporté")
    except Exception as e:
        print(f"[stripe_webhook_checkout_completed] Erreur générale : {str(e)}")
        return HttpResponse(status=400, body=str(e))


@csrf_exempt
@require_POST
def stripe_webhook_subscription_deleted(request):
    print("[stripe_webhook_subscription_deleted] Début du traitement du webhook")
    payload = request.body
    sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
    
    if not sig_header:
        print("[stripe_webhook_subscription_deleted] En-tête de signature Stripe manquant")
        return HttpResponse("En-tête de signature Stripe manquant", status=401)
    
    try:
        print("[stripe_webhook_subscription_deleted] Vérification de la signature")
        event = verify_stripe_signature(payload, sig_header, STRIPE_WEBHOOK_SECRET_SUBSCRIPTION_DELETED)
        if not event:
            print("[stripe_webhook_subscription_deleted] Erreur de vérification de la signature")
            return HttpResponse("Erreur de vérification de la signature", status=401)
        
        if event['type'] == 'customer.subscription.deleted':
            subscription = event['data']['object']
            print("[stripe_webhook_subscription_deleted] Subscription reçue : ", subscription)
            
            # Récupération des informations du client
            customer_id = subscription.get('customer')
            if not customer_id:
                print("[stripe_webhook_subscription_deleted] Customer ID manquant")
                return HttpResponse("Customer ID manquant", status=400)
            
            try:
                # Récupération des informations du client depuis Stripe
                print(f"[stripe_webhook_subscription_deleted] Récupération du customer {customer_id}")
                customer = stripe.Customer.retrieve(customer_id)
                
                # Création d'une session simulée pour utiliser get_user_from_session
                session = {
                    'customer': customer_id,
                    'customer_email': customer.email,
                    'customer_details': {'email': customer.email}
                }
                
                # Récupération de l'utilisateur via la fonction utilitaire
                print("[stripe_webhook_subscription_deleted] Récupération de l'utilisateur")
                user = get_user_from_session(session)
                if not user:
                    print("[stripe_webhook_subscription_deleted] Utilisateur non trouvé")
                    return HttpResponse("Utilisateur non trouvé", status=404)
                
                product_id = subscription.get('items', {}).get('data', [{}])[0].get('price', {}).get('product')
                if product_id and is_same_product(user, products[product_id]): # Gére les cas de mise à jour
                    # Mise à jour de l'utilisateur pour l'abonnement free
                    new_abonnement = abonnements()["free"]
                    print("[stripe_webhook_subscription_deleted] Passage à l'abonnement gratuit")
                    modify_user_abonnement(user, new_abonnement)
                
            except Exception as e:
                print(f"[stripe_webhook_subscription_deleted] Erreur lors du traitement de l'annulation : {str(e)}")
                return HttpResponse(f"Erreur lors du traitement : {str(e)}", status=400)
            
            print("[stripe_webhook_subscription_deleted] Traitement terminé avec succès")
            return HttpResponse(status=200)
            
        return HttpResponse("Type d'événement non supporté", status=400)
    except Exception as e:
        print(f"[stripe_webhook_subscription_deleted] Erreur générale : {str(e)}")
        return HttpResponse(f"Erreur : {str(e)}", status=400)



@csrf_exempt
@require_POST
def stripe_webhook_subscription_updated(request):
    payload = request.body
    sig_header = request.META['HTTP_STRIPE_SIGNATURE']
    
    try:
        event = verify_stripe_signature(payload, sig_header)
        if not event:
            return HttpResponse(status=401, body="Erreur de vérification de la signature")
        
        if event['type'] == 'customer.subscription.updated':
            subscription = event['data']['object']
            
            # Récupération des informations du client
            customer_id = subscription.get('customer')
            if not customer_id:
                return HttpResponse("Customer ID manquant", status=400)
            
            try:
                # Récupération des informations du client depuis Stripe
                customer = stripe.Customer.retrieve(customer_id)
                
                # Création d'une session simulée pour utiliser get_user_from_session
                session = {
                    'customer': customer_id,
                    'customer_email': customer.email,
                    'customer_details': {'email': customer.email}
                }
                
                # Récupération de l'utilisateur via la fonction utilitaire
                user = get_user_from_session(session)
                if not user:
                    return HttpResponse("Utilisateur non trouvé", status=404)
                
                # Récupération du produit associé à l'abonnement
                product_id = subscription.get('items', {}).get('data', [{}])[0].get('price', {}).get('product')
                if not product_id or product_id not in products:
                    return HttpResponse("Produit non reconnu", status=400)
                
                # Mise à jour de l'utilisateur avec le nouvel abonnement
                product_type = products[product_id]
                new_abonnement = abonnements()[product_type]
                modify_user_abonnement(user, new_abonnement)
                
            except Exception as e:
                print(f"Erreur lors du traitement de la mise à jour : {str(e)}")
                return HttpResponse("Erreur lors du traitement", status=400)
            
            return HttpResponse(status=200)
            
        return HttpResponse(status=400, body="Type d'événement non supporté")
    except Exception as e:
        return HttpResponse(status=400, body=str(e))

# On ouvre la page de paiement
# Lorsqu'on reçoit un "subscription_updated", on renvoie une requête à l'api du ront
# L'api du ront met à jour la page de paiement

@csrf_exempt
@api_view(['POST'])
@authentication_classes([JWTAuthenticationWithUserCheck])
@permission_classes([IsAuthenticated])
def log_error(request):
    """Vue pour journaliser les erreurs de l'application par utilisateur"""
    try:
        # Récupération des données du rapport d'erreur
        data = json.loads(request.body)
        user_pid = request.user.publicIdentifier
        status = data.get('status', 0)
        level = data.get('level', 'error')
        message = data.get('message')
        
        if not user_pid or not message:
            return HttpResponse("Données manquantes, vous devez indiquer un champ message et fournir un token d'accès valide", status=400)
            
        write_error_log(user_pid, message, status=status, level=level)
            
        return HttpResponse(status=200)
        
    except Exception as e:
        return HttpResponse(f"Erreur lors de la journalisation : {str(e)}", status=500)

