from django.db.models.signals import pre_save, post_delete
from django.dispatch import receiver
from django.core.mail import send_mail
from django.conf import settings
from django.urls import reverse
from .models import Order, OrderItem, OrderStatus
from django.db import transaction
from django.contrib.auth.signals import user_logged_in
from .cart import transfer_cart

@receiver([pre_save, post_delete], sender=Order)
def send_order_notification(sender, instance, **kwargs):
    """Send an email notification when an order is created, updated, or deleted."""
    # For pre_save, we need to check if the instance exists in the database
    if kwargs.get('signal', None) == pre_save:
        try:
            # Get the current instance from the database
            old_instance = Order.objects.get(pk=instance.pk)
            # Only send notification if status has changed
            if old_instance.status != instance.status:
                # Execute on commit to ensure DB consistency
                transaction.on_commit(lambda: _send_order_email(instance, old_status=old_instance.status, **kwargs))
        except Order.DoesNotExist:
            # This is a new instance (creation)
            # You might want to handle creation differently or just pass
            pass
    else:
        # Handle post_delete
        transaction.on_commit(lambda: _send_order_email(instance, **kwargs))

def _send_order_email(instance, old_status=None, **kwargs):
    # Skip processing for PENDING orders
    if instance.status == OrderStatus.PENDING:
        return

    # Determine action type
    if 'signal' in kwargs and kwargs['signal'] == post_delete:
        action = "DELETED"
    elif old_status is None:
        action = "CREATED"
    else:
        action = "UPDATED"
    if action in ["DELETED", "CREATED"]:
        return  # No emails for these actions

    # Build common email components
    order_details = _get_order_details(instance)
    items_list = _build_items_list(instance.id, action)

    # Send internal notification
    _send_internal_notification(instance, action, order_details, items_list)

    # Send customer notification if applicable
    if order_details['customer_email']:
        _send_customer_notification(instance, order_details, items_list)

def _get_order_details(instance):
    """Extract and build order details dictionary."""
    # Get customer info
    customer_email = None
    if instance.user:
        customer_info = f"Utilisateur: {instance.user.email}"
        customer_email = instance.user.email
    elif instance.guest_user:
        customer_info = f"Invité: {instance.guest_user.email} ({instance.guest_user.phone})"
        customer_email = instance.guest_user.email
    else:
        customer_info = "Client inconnu"

    # Translate statuses
    status_fr_map = {
        "PENDING": "EN ATTENTE", "PAID": "PAYÉE",
        "SHIPPED": "EXPÉDIÉE", "DELIVERED": "LIVRÉE", "CANCELED": "ANNULÉE", "REFUNDED": "REMBOURSÉE", "PREPARED": "EN COURS DE PRÉPARATION", "READY": "PRÊT"
    }

    payment_status_fr_map = {
        "UNPAID": "NON PAYÉE", "PAID": "PAYÉE", "FAILED": "ÉCHOUÉE", "REFUNDED": "REMBOURSÉE",
    }

    # Calculate discount information
    has_coupon = instance.coupon is not None
    coupon_info = ""
    final_price = instance.total_price

    if has_coupon:
        coupon_code = instance.coupon.code
        discount_amount = instance.discount_amount
        final_price = instance.get_total_after_discount()

        if instance.coupon.discount_percent > 0:
            coupon_info = f"Code promo: {coupon_code} ({instance.coupon.discount_percent}%)"
        else:
            coupon_info = f"Code promo: {coupon_code} (€{discount_amount})"

    return {
        'order_id': instance.id,
        'status': instance.status,
        'status_fr': status_fr_map.get(instance.status, instance.status),
        'payment_status': instance.payment_status,
        'payment_status_fr': payment_status_fr_map.get(instance.payment_status, instance.payment_status),
        'total_price': instance.total_price,
        'has_coupon': has_coupon,
        'coupon_info': coupon_info,
        'discount_amount': instance.discount_amount if has_coupon else 0,
        'final_price': final_price,
        'customer_info': customer_info,
        'customer_email': customer_email,
        'date_ordered': instance.date_ordered,
        'admin_url': f"{settings.SHOP_SITE_ROOT_URL}{reverse('admin:shop_order_change', args=[instance.id])}",
        'shipping_address': instance.get_shipping_address(),
    }

def _build_items_list(order_id, action):
    """Build the list of order items."""
    items_list = ""
    if action != "DELETED":
        order_items = OrderItem.objects.filter(order_id=order_id).select_related('product', 'color', 'size')
        for item in order_items:
            color = item.color.name if item.color else "N/A"
            size = item.size.name if item.size else "N/A"
            items_list += f"- {item.quantity}x {item.product.title} (Couleur: {color}, Taille: {size}, Prix: {item.price}€)\n"
    return items_list

def _translate_action(action):
    """Translate action to French."""
    translations = {
        "CREATED": "CRÉÉE", "UPDATED": "MISE À JOUR", "DELETED": "SUPPRIMÉE"
    }
    return translations.get(action, action)

def _send_internal_notification(instance, action, order_details, items_list):
    """Send notification email to shop managers."""
    action_fr = _translate_action(action)

    # Build price information with coupon details if applicable
    price_info = f"Prix total: {order_details['total_price']}€"
    server = ""
    if settings.DEBUG:
        server = "DEBUG: "

    if order_details['has_coupon']:
        price_info = f"""
Prix total: {order_details['total_price']}€
{order_details['coupon_info']}
Réduction: -{order_details['discount_amount']}€
Montant payé: {order_details['final_price']}€"""
    subject = f"{server}Commande #{order_details['order_id']} {action_fr}: {order_details['status_fr']}"
    message = f"""
La commande #{order_details['order_id']} a été {action_fr.lower()}

Statut: {order_details['status_fr']}
Statut de paiement: {order_details['payment_status_fr']}
{price_info}

{order_details['customer_info']}

{order_details['shipping_address']}

Articles:
{items_list}

Voir la commande dans le panneau d'administration: {order_details['admin_url']}

Ceci est un message automatique. Merci de ne pas répondre.
    """

    # Send internal email
    recipient_list = [email for name, email in settings.SHOP_MANAGERS]
    if not recipient_list:
        recipient_list = [settings.DEFAULT_FROM_EMAIL]

    send_mail(
        subject=subject,
        message=message,
        from_email=settings.DEFAULT_FROM_EMAIL,
        recipient_list=recipient_list,
        fail_silently=False,
    )

def _send_customer_notification(instance, order_details, items_list):
    """Send appropriate notification email to customer based on order status."""
    # Common email variables
    contact_email = settings.SHOP_SUPPORT_EMAIL
    shop_url = f"{settings.SHOP_SITE_ROOT_URL}/shop"
    date_formatted = order_details['date_ordered'].strftime('%d/%m/%Y')

    # Determine email content based on status and payment status
    email_content = _get_customer_email_content(
        instance.status,
        order_details['payment_status'],
        order_details['order_id'],
        date_formatted,
        order_details['status_fr'],
        order_details['total_price'],
        order_details['has_coupon'],
        order_details['coupon_info'],
        order_details['discount_amount'],
        order_details['final_price'],
        items_list,
        contact_email,
        shop_url,
        order_details['shipping_address']
    )

    # Skip if no email content returned
    if not email_content:
        return

    # Send email to customer
    send_mail(
        subject=email_content['subject'],
        message=email_content['message'],
        from_email=settings.DEFAULT_FROM_EMAIL,
        recipient_list=[order_details['customer_email']],
        fail_silently=False,
    )

def _get_customer_email_content(status, payment_status, order_id, date, status_fr,
                               total_price, has_coupon, coupon_info, discount_amount,
                               final_price, items_list, contact_email, shop_url, shipping_address):
    """Get the appropriate customer email content based on order status."""

    # Build price information with coupon details if applicable
    price_info = f"Prix total: {total_price}€"
    if has_coupon:
        price_info = f"""Prix total: {total_price}€
{coupon_info}
Réduction: -{discount_amount}€
Montant payé: {final_price}€"""

    # Payment confirmation email
    if status == OrderStatus.PAID and payment_status == "PAID":
        return {
            'subject': f"Confirmation de votre commande #{order_id} - Padel Club",
            'message': _build_payment_confirmation_email(order_id, date, status_fr,
                                                       price_info, items_list,
                                                       contact_email, shop_url, shipping_address)
        }

    # Order status update email
    elif status in [OrderStatus.SHIPPED, OrderStatus.DELIVERED, OrderStatus.CANCELED, OrderStatus.PREPARED, OrderStatus.REFUNDED, OrderStatus.READY]:
        status_message = {
            OrderStatus.PREPARED: "Votre commande est en cours de préparation.",
            OrderStatus.READY: "Votre commande est prête.",
            OrderStatus.SHIPPED: "Votre commande a été expédiée et est en cours de livraison.",
            OrderStatus.DELIVERED: "Votre commande a été livrée. Nous espérons que vous apprécierez vos produits !",
            OrderStatus.REFUNDED: "Votre commande a été annulée et son remboursement est en cours de traitement.",
            OrderStatus.CANCELED: "Votre commande a été annulée. Si vous n'êtes pas à l'origine de cette annulation, veuillez nous contacter immédiatement."
        }.get(status, "")

        return {
            'subject': f"Mise à jour de votre commande #{order_id} - Padel Club",
            'message': _build_status_update_email(order_id, date, status_message, status_fr,
                                                 price_info, items_list, contact_email, shipping_address)
        }

    # Payment issue notification
    elif payment_status == "FAILED":
        return {
            'subject': f"Problème de paiement pour votre commande #{order_id} - Padel Club",
            'message': _build_payment_issue_email(order_id, date, price_info,
                                                 items_list, contact_email, shop_url)
        }

    # Payment reminder for unpaid orders
    elif payment_status == "UNPAID" and status != OrderStatus.PENDING:
        return {
            'subject': f"Rappel de paiement pour votre commande #{order_id} - Padel Club",
            'message': _build_payment_reminder_email(order_id, date, price_info,
                                                    items_list, contact_email)
        }

    # No email needed
    return None

def _build_payment_confirmation_email(order_id, date, status_fr, price_info, items_list, contact_email, shop_url, shipping_address):
    """Build payment confirmation email message."""
    return f"""
Bonjour,

Nous vous remercions pour votre commande sur Padel Club !

Récapitulatif de votre commande #{order_id} du {date} :

Statut: {status_fr}
{price_info}

Détail de votre commande :
{items_list}

IMPORTANT - COMMENT RÉCUPÉRER VOTRE COMMANDE :
Notre boutique fonctionne entre amis 'Padel Club'.
Nous allons préparer votre commande et vous la remettre en main propre lors d'une prochaine session de padel !
Si jamais la livraison est possible, nous vous expédierons votre commande à l'adresse indiquée. Vous serez alors notifiés par email lorsque votre commande sera expédiée.
{shipping_address}

Pour toute question concernant votre commande, n'hésitez pas à contacter notre service client :
{contact_email}

Visitez notre boutique pour découvrir d'autres produits :
{shop_url}

Merci de votre confiance et à bientôt sur Padel Club !

L'équipe Padel Club
    """

def _build_status_update_email(order_id, date, status_message, status_fr, price_info, items_list, contact_email, shipping_address):
    """Build status update email message."""
    return f"""
Bonjour,

Mise à jour concernant votre commande Padel Club #{order_id} du {date} :

{status_message}
{shipping_address}

Statut actuel: {status_fr}
{price_info}

Détail de votre commande :
{items_list}

Pour toute question concernant votre commande, n'hésitez pas à contacter notre service client :
{contact_email}

Merci de votre confiance et à bientôt sur Padel Club !

L'équipe Padel Club
    """

def _build_payment_issue_email(order_id, date, price_info, items_list, contact_email, shop_url):
    """Build payment issue email message."""
    return f"""
Bonjour,

Nous avons rencontré un problème lors du traitement du paiement de votre commande Padel Club #{order_id}.

Détails de la commande :
Date: {date}
{price_info}

Articles:
{items_list}

Veuillez vérifier vos informations de paiement et réessayer. Si le problème persiste, n'hésitez pas à contacter notre service client :
{contact_email}

Vous pouvez également visiter notre boutique pour finaliser votre achat :
{shop_url}

Merci de votre compréhension.

L'équipe Padel Club
    """

def _build_payment_reminder_email(order_id, date, price_info, items_list, contact_email):
    """Build payment reminder email message."""
    return f"""
Bonjour,

Nous vous rappelons que votre commande Padel Club #{order_id} du {date} n'a pas encore été payée.

Détails de la commande :
{price_info}

Articles:
{items_list}

Pour finaliser votre commande, veuillez procéder au paiement dès que possible.

Si vous rencontrez des difficultés ou si vous avez des questions, n'hésitez pas à contacter notre service client :
{contact_email}

Merci de votre confiance.

L'équipe Padel Club
    """

@receiver(user_logged_in)
def user_logged_in_handler(sender, request, user, **kwargs):
    """
    When a user logs in, transfer any cart items from their anonymous session
    """
    # Get the anonymous session key
    if hasattr(request, 'session') and not request.session.is_empty():
        anonymous_session_key = request.session.session_key

        # After the user logs in, the session key changes
        # So we transfer cart from the old session to the new session
        if anonymous_session_key:
            transfer_cart(request, anonymous_session_key)
