"""
Notifications event listener.

Acts as the single dispatch point for all outbound notifications.
All notification triggering routes through notification.email_requested
and notification.sms_requested events so that delivery is decoupled from
business logic.

Events handled:
  notification.email_requested → resolve template and send via GmailProvider
  notification.sms_requested   → resolve template and send via TwilioProvider
  payment_link.created         → emit notification.email_requested + sms_requested
  customer.account_created     → emit notification.email_requested
"""

import logging
from typing import Any

from src.core.database import SessionCelery
from src.events.base import BaseEvent
from src.events.dispatcher import on_event_async

logger = logging.getLogger(__name__)


@on_event_async("notification.email_requested")
async def send_email(event: BaseEvent) -> None:
    """
    Resolve the notification template and dispatch via GmailProvider.
    """
    from src.apps.notifications.schemas.notification_schemas import SendNotificationSchema
    from src.apps.notifications.services import send_notification

    data: Any = event.data
    to_email = data.get("to_email")
    template_key = data.get("template_key")
    template_vars = data.get("template_vars", {})
    merchant_id = data.get("merchant_id")

    if not to_email or not template_key:
        logger.warning(
            "send_email: missing required fields. Present keys: %s", list(data.keys())
        )
        return

    try:
        with SessionCelery() as db:
            await send_notification(
                db,
                SendNotificationSchema(
                    template_key=template_key,
                    channel="email",
                    recipient_email=to_email,
                    template_vars=template_vars,
                    merchant_id=merchant_id,
                ),
            )
    except Exception as exc:
        logger.error(
            "send_email handler failed [template=%s to=%s]: %s",
            template_key,
            to_email,
            exc,
        )


@on_event_async("notification.sms_requested")
async def send_sms(event: BaseEvent) -> None:
    """
    Resolve the notification template and dispatch via TwilioProvider.
    """
    from src.apps.notifications.schemas.notification_schemas import SendNotificationSchema
    from src.apps.notifications.services import send_notification

    data: Any = event.data
    to_phone = data.get("to_phone")
    template_key = data.get("template_key")
    template_vars = data.get("template_vars", {})
    merchant_id = data.get("merchant_id")

    if not to_phone or not template_key:
        logger.warning(
            "send_sms: missing to_phone or template_key in event data: %s", data
        )
        return

    try:
        with SessionCelery() as db:
            await send_notification(
                db,
                SendNotificationSchema(
                    template_key=template_key,
                    channel="sms",
                    recipient_phone=to_phone,
                    template_vars=template_vars,
                    merchant_id=merchant_id,
                ),
            )
    except Exception as exc:
        logger.error(
            "send_sms handler failed [template=%s to=%s]: %s",
            template_key,
            to_phone,
            exc,
        )


@on_event_async("payment_link.created")
async def handle_payment_link_created(event: BaseEvent) -> None:
    """
    Send the HPP payment-request notification to the customer when a link
    is created or resent.

    Emits notification.email_requested and notification.sms_requested instead
    of calling the notification service directly.
    """
    from sqlalchemy import select

    from src.apps.payment_requests.models.payment_request import PaymentRequest
    from src.core.config import settings
    from src.events.dispatcher import EventDispatcher

    data: Any = event.data
    payment_request_id = data.get("payment_request_id")
    token = data.get("token", "")

    if not payment_request_id:
        logger.warning("payment_link.created event missing payment_request_id")
        return

    hpp_link = f"{settings.hpp_frontend_base_url}/hpp?token={token}"

    try:
        with SessionCelery() as db:
            stmt = select(PaymentRequest).where(
                PaymentRequest.id == payment_request_id,
                PaymentRequest.deleted_at.is_(None),
            )
            pr = db.execute(stmt).scalar_one_or_none()
            if not pr:
                logger.warning(
                    "payment_link.created: PR %s not found", payment_request_id
                )
                return

            merchant = getattr(pr, "merchant", None)
            merchant_name = getattr(merchant, "name", "") if merchant else ""

            customer_email = None
            customer_phone = None
            for pr_customer in getattr(pr, "payment_request_customers", []):
                cust = getattr(pr_customer, "customer", None)
                if cust:
                    customer_email = getattr(cust, "email", None)
                    customer_phone = getattr(cust, "phone", None) or getattr(
                        cust, "phone_number", None
                    )
                    break

            due_date_str = pr.due_date.strftime("%Y-%m-%d") if pr.due_date else None
            template_vars = {
                "merchant_name": merchant_name,
                "amount": f"{float(pr.amount or 0):.2f}",
                "currency": pr.currency or "USD",
                "due_date": due_date_str or "",
                "hpp_link": hpp_link,
            }
            merchant_id = pr.merchant_id

        if customer_email:
            await EventDispatcher.dispatch(
                BaseEvent(
                    event_type="notification.email_requested",
                    data={
                        "to_email": customer_email,
                        "template_key": "hpp_payment_request",
                        "template_vars": template_vars,
                        "merchant_id": merchant_id,
                    },
                    correlation_id=event.correlation_id,
                )
            )

        if customer_phone:
            await EventDispatcher.dispatch(
                BaseEvent(
                    event_type="notification.sms_requested",
                    data={
                        "to_phone": customer_phone,
                        "template_key": "hpp_payment_request",
                        "template_vars": template_vars,
                        "merchant_id": merchant_id,
                    },
                    correlation_id=event.correlation_id,
                )
            )

    except Exception as exc:
        logger.error("handle_payment_link_created failed: %s", exc)


@on_event_async("checkout.payment_completed")
async def handle_checkout_payment_completed(event: BaseEvent) -> None:
    """
    Send a payment receipt to the payer after a successful checkout payment.
    Emits notification.email_requested to the payer's email.
    """
    from sqlalchemy import select

    from src.apps.checkouts.models.checkout import Checkout
    from src.apps.payment_requests.models.payment_request import PaymentRequest
    from src.apps.users.models.user import User
    from src.core.config import settings
    from src.events.dispatcher import EventDispatcher

    data: Any = event.data
    checkout_id = data.get("checkout_id")
    payment_request_id = data.get("payment_request_id")

    if not checkout_id:
        logger.warning("checkout.payment_completed missing checkout_id")
        return

    try:
        with SessionCelery() as db:
            checkout = db.execute(
                select(Checkout).where(
                    Checkout.id == checkout_id,
                    Checkout.deleted_at.is_(None),
                )
            ).scalar_one_or_none()
            if not checkout:
                logger.warning("checkout.payment_completed: checkout %s not found", checkout_id)
                return

            pr = None
            if payment_request_id:
                pr = db.execute(
                    select(PaymentRequest).where(
                        PaymentRequest.id == payment_request_id,
                        PaymentRequest.deleted_at.is_(None),
                    )
                ).scalar_one_or_none()

            # Fetch payer email from payment request's created_by user
            payer_email = None
            if pr and pr.created_by_id:
                payer = db.execute(
                    select(User).where(
                        User.id == pr.created_by_id,
                        User.deleted_at.is_(None),
                    )
                ).scalar_one_or_none()
                if payer:
                    payer_email = payer.email

            amount = float(getattr(pr, "amount", 0) or 0) if pr else 0.0
            currency = getattr(pr, "currency", "USD") if pr else "USD"
            checkout_url = f"{settings.hpp_frontend_base_url}/c"

            template_vars = {
                "checkout_title": checkout.title or "Payment",
                "amount": f"{amount:.2f}",
                "currency": currency,
                "checkout_literal": checkout.checkout_literal,
                "checkout_url": checkout_url,
            }
            merchant_id = checkout.merchant_id

        if payer_email:
            await EventDispatcher.dispatch(
                BaseEvent(
                    event_type="notification.email_requested",
                    data={
                        "to_email": payer_email,
                        "template_key": "checkout_payment_receipt",
                        "template_vars": template_vars,
                        "merchant_id": merchant_id,
                    },
                    correlation_id=event.correlation_id,
                )
            )

    except Exception as exc:
        logger.error("handle_checkout_payment_completed failed: %s", exc)


@on_event_async("checkout.shared")
async def handle_checkout_shared(event: BaseEvent) -> None:
    """
    Dispatch email/SMS notifications to recipients when a checkout link is shared.
    """
    from sqlalchemy import select

    from src.apps.checkouts.models.checkout import Checkout
    from src.apps.checkouts.crud import get_active_link
    from src.core.config import settings
    from src.events.dispatcher import EventDispatcher

    data: Any = event.data
    checkout_id = data.get("checkout_id")
    recipients = data.get("recipients", {})
    merchant_id = data.get("merchant_id")

    if not checkout_id:
        logger.warning("checkout.shared missing checkout_id")
        return

    try:
        with SessionCelery() as db:
            checkout = db.execute(
                select(Checkout).where(
                    Checkout.id == checkout_id,
                    Checkout.deleted_at.is_(None),
                )
            ).scalar_one_or_none()
            if not checkout:
                logger.warning("checkout.shared: checkout %s not found", checkout_id)
                return

            active_link = get_active_link(db, checkout_id)
            token = active_link.token if active_link else ""
            checkout_url = f"{settings.hpp_frontend_base_url}/c/{token}"
            template_vars = {
                "checkout_title": checkout.title or "Payment Link",
                "checkout_url": checkout_url,
                "checkout_literal": checkout.checkout_literal,
            }

        # Email recipients
        email_recipients: list = recipients.get("email_recipients", [])
        for email in email_recipients:
            if email:
                await EventDispatcher.dispatch(
                    BaseEvent(
                        event_type="notification.email_requested",
                        data={
                            "to_email": email,
                            "template_key": "checkout_shared",
                            "template_vars": template_vars,
                            "merchant_id": merchant_id,
                        },
                        correlation_id=event.correlation_id,
                    )
                )

        # SMS recipients
        sms_recipients: list = recipients.get("sms_recipients", [])
        for phone in sms_recipients:
            if phone:
                await EventDispatcher.dispatch(
                    BaseEvent(
                        event_type="notification.sms_requested",
                        data={
                            "to_phone": phone,
                            "template_key": "checkout_shared",
                            "template_vars": template_vars,
                            "merchant_id": merchant_id,
                        },
                        correlation_id=event.correlation_id,
                    )
                )

    except Exception as exc:
        logger.error("handle_checkout_shared failed: %s", exc)


@on_event_async("customer.account_created")
async def handle_customer_account_created(event: BaseEvent) -> None:
    """
    Send a welcome + email-verification notification after a customer registers.

    Generates a JWT verification token and emits notification.email_requested
    instead of calling the notification service directly.
    """
    from datetime import datetime, timedelta, timezone
    from sqlalchemy import select

    import jwt as pyjwt

    from src.apps.merchants.models.merchant import Merchant
    from src.apps.users.models.user import User
    from src.core.config import settings
    from src.events.dispatcher import EventDispatcher

    data: Any = event.data
    user_id = data.get("user_id")
    email = data.get("email", "")
    merchant_id = data.get("merchant_id")

    if not user_id or not email:
        return

    try:
        with SessionCelery() as db:
            # Generate verification token (JWT, 24h)
            expires_at = datetime.now(timezone.utc) + timedelta(hours=24)
            verify_payload = {
                "sub": str(user_id),
                "email": email,
                "purpose": "email_verify",
                "exp": expires_at,
            }
            verify_token = pyjwt.encode(
                verify_payload,
                settings.JWT_SECRET_KEY,
                algorithm=settings.JWT_ALGORITHM,
            )
            # Use HPP_FRONTEND_BASE_URL for customer-facing verify-email links.
            verify_link = (
                f"{settings.hpp_frontend_base_url}"
                f"/hpp/customer/verify-email?token={verify_token}"
            )

            # Fetch user name
            stmt = select(User).where(User.id == user_id, User.deleted_at.is_(None))
            user = db.execute(stmt).scalar_one_or_none()
            customer_name = (
                user.full_name or user.first_name or email.split("@")[0]
                if user
                else email.split("@")[0]
            )

            # Fetch merchant name
            merchant_name = ""
            if merchant_id:
                merchant_stmt = select(Merchant).where(Merchant.id == merchant_id)
                merchant = db.execute(merchant_stmt).scalar_one_or_none()
                if merchant:
                    merchant_name = getattr(merchant, "name", "")

        await EventDispatcher.dispatch(
            BaseEvent(
                event_type="notification.email_requested",
                data={
                    "to_email": email,
                    "template_key": "merchant_welcome",
                    "template_vars": {
                        "merchant_name": merchant_name,
                        "customer_name": customer_name,
                        "verify_link": verify_link,
                    },
                    "merchant_id": merchant_id,
                },
                correlation_id=event.correlation_id,
            )
        )

    except Exception as exc:
        logger.error("handle_customer_account_created notification failed: %s", exc)
