"""
store/signals.py
────────────────
Post-payment Django signals.

Connected in StoreConfig.ready() (store/apps.py) so they register once
at startup without any extra wiring needed.

Signal handlers
───────────────
handle_order_status_change
    Fires on every Order save.  When the status transitions to "paid",
    triggers a confirmation email to the customer.

handle_order_cancelled_stock_release
    Fires when an order transitions to "cancelled" (e.g. admin action or
    a future cancel endpoint).  Releases the reserved stock back to the books.
    Note: failed orders from the M-Pesa callback release stock inline in
    services/payment_service.py because it runs inside the same transaction
    as the callback processing.  This signal handles the admin-cancel case.
"""

import logging

from django.conf import settings
from django.core.mail import send_mail
from django.db.models import F
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

from catalog_app.models import Book
from .email_utils import BRAND, kv_row, render_email_shell, section_label
from .models import Order

logger = logging.getLogger(__name__)

# Cache the previous status so we can detect transitions
_ORDER_PREV_STATUS: dict[int, str] = {}


@receiver(pre_save, sender=Order)
def _cache_previous_status(sender, instance: Order, **kwargs) -> None:
    """
    Store the current DB status before the save so post_save can detect
    a status transition (e.g. pending → paid, pending → cancelled).
    """
    if instance.pk:
        try:
            _ORDER_PREV_STATUS[instance.pk] = (
                Order.objects.values_list("status", flat=True).get(pk=instance.pk)
            )
        except Order.DoesNotExist:
            pass


@receiver(post_save, sender=Order)
def handle_order_status_change(
    sender, instance: Order, created: bool, **kwargs
) -> None:
    """
    React to Order status transitions.

    Transitions handled:
      → paid       : send confirmation email to customer
      → cancelled  : release reserved stock
    """
    prev_status = _ORDER_PREV_STATUS.pop(instance.pk, None)

    if created:
        return  # Nothing to do right after creation

    if prev_status != Order.STATUS_PAID and instance.status == Order.STATUS_PAID:
        _send_payment_confirmation(instance)

    # A failed order's stock was already released when the payment failed.
    if (
        prev_status not in (Order.STATUS_CANCELLED, Order.STATUS_FAILED, None)
        and instance.status == Order.STATUS_CANCELLED
    ):
        _release_stock_on_cancel(instance)


# ---------------------------------------------------------------------------
# Private helpers
# ---------------------------------------------------------------------------

def _send_payment_confirmation(order: Order) -> None:
    """
    Send an order confirmation email to the customer.
    Silently skips if customer_email is empty or email backend fails.
    """
    if not order.customer_email:
        return

    try:
        items = order.items.select_related("book").all()
        items_lines = "\n".join(
            f"  • {item.quantity}× {item.book.title}  — KES {item.total_price:,.2f}"
            for item in items
        )

        delivery_info = ""
        if order.delivery_type == Order.DELIVERY_HOME and order.delivery_address:
            addr = order.delivery_address
            delivery_info = (
                f"Delivery address : {addr.get('street', '')}, "
                f"{addr.get('area', '')}, {addr.get('city', '')}\n"
                f"Estimated arrival: {order.delivery_area.estimated_days} business days"
                if order.delivery_area else ""
            )
        elif order.delivery_type == Order.DELIVERY_PICKUP and order.pickup_location:
            loc = order.pickup_location
            delivery_info = (
                f"Pickup location  : {loc.name}\n"
                f"Address          : {loc.address}\n"
                f"Opening hours    : {loc.opening_hours}"
            )

        contact_email = settings.SUPPORT_CONTACT_EMAIL

        subject = f"Order Confirmed – {order.order_reference}"
        body = f"""Dear {order.customer_name},

Thank you for your purchase from Tai AfroPress! Your order has been confirmed.

─────────────────────────────────────
Order Reference : {order.order_reference}
M-Pesa Receipt  : {order.mpesa_receipt_number}
Amount Paid     : KES {order.final_amount:,.2f}
─────────────────────────────────────

Items ordered:
{items_lines}

{delivery_info}

We'll keep you updated as your order is processed. Do not reply to this
email, instead you can reach us at {contact_email}
if you have any questions.

Warm regards,
Tai AfroPress Team
"""

        items_rows_html = "".join(
            f"""<tr>
                <td style="padding:9px 0;border-bottom:1px solid {BRAND['border']};font-size:14px;color:{BRAND['text']};">{item.quantity}&times; {item.book.title}</td>
                <td style="padding:9px 0;border-bottom:1px solid {BRAND['border']};font-size:14px;color:{BRAND['green']};font-weight:700;text-align:right;white-space:nowrap;">KES {item.total_price:,.2f}</td>
            </tr>"""
            for item in items
        )

        delivery_html = ""
        if order.delivery_type == Order.DELIVERY_HOME and order.delivery_address and order.delivery_area:
            addr = order.delivery_address
            delivery_html = f"""
            <div style="background:{BRAND['bg']};border:1px solid {BRAND['border']};border-radius:10px;padding:16px 18px;margin-top:20px;">
              {section_label("Delivery")}
              <div style="font-size:14px;color:{BRAND['text']};">{addr.get('street', '')}, {addr.get('area', '')}, {addr.get('city', '')}</div>
              <div style="font-size:13px;color:{BRAND['muted']};margin-top:4px;">Estimated arrival: {order.delivery_area.estimated_days} business days</div>
            </div>"""
        elif order.delivery_type == Order.DELIVERY_PICKUP and order.pickup_location:
            loc = order.pickup_location
            delivery_html = f"""
            <div style="background:{BRAND['bg']};border:1px solid {BRAND['border']};border-radius:10px;padding:16px 18px;margin-top:20px;">
              {section_label("Pickup Location")}
              <div style="font-size:14px;font-weight:700;color:{BRAND['text']};">{loc.name}</div>
              <div style="font-size:13px;color:{BRAND['muted']};margin-top:2px;">{loc.address}</div>
              <div style="font-size:13px;color:{BRAND['muted']};">{loc.opening_hours}</div>
            </div>"""

        html_body = render_email_shell(
            heading="Your order is confirmed!",
            body_html=f"""
            <p style="margin:0 0 16px;">Dear {order.customer_name},</p>
            <p style="margin:0 0 20px;">Thank you for your purchase from Tai AfroPress! Your order has been confirmed and is being prepared.</p>
            <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:{BRAND['bg']};border:1px solid {BRAND['border']};border-radius:10px;margin-bottom:22px;">
              <tr><td style="padding:16px 18px;">
                <table role="presentation" width="100%" cellpadding="0" cellspacing="0">
                  {kv_row("Order Reference", order.order_reference)}
                  {kv_row("M-Pesa Receipt", order.mpesa_receipt_number)}
                  {kv_row("Amount Paid", f"KES {order.final_amount:,.2f}", strong=True, top_border=True)}
                </table>
              </td></tr>
            </table>
            {section_label("Items Ordered")}
            <table role="presentation" width="100%" cellpadding="0" cellspacing="0">{items_rows_html}</table>
            {delivery_html}
            <p style="margin:24px 0 0;font-size:13px;color:{BRAND['muted']};">We'll keep you updated as your order is processed. Do not reply to this email &mdash; instead reach us at <a href="mailto:{contact_email}" style="color:{BRAND['green']};">{contact_email}</a> if you have any questions.</p>
            <p style="margin:18px 0 0;font-size:14px;color:{BRAND['text']};">Warm regards,<br>Tai AfroPress Team</p>
            """,
        )

        send_mail(
            subject=subject,
            message=body,
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[order.customer_email],
            fail_silently=True,
            html_message=html_body,
        )
        logger.info(
            "Confirmation email sent | order=%s | to=%s",
            order.order_reference, order.customer_email,
        )
    except Exception as exc:
        # Never let an email error bubble up and corrupt the payment flow
        logger.error(
            "Failed to send confirmation email for order %s: %s",
            order.order_reference, exc,
        )


def _release_stock_on_cancel(order: Order) -> None:
    """
    Return reserved stock to books when an order is cancelled.
    Runs outside the M-Pesa callback transaction (admin cancellations).
    """
    released = 0
    for item in order.items.all():
        Book.objects.filter(pk=item.book_id).update(stock=F("stock") + item.quantity)
        released += 1

    logger.info(
        "Stock released for %d item(s) on cancelled order %s",
        released, order.order_reference,
    )
