"""
Notification services - Multi-channel notification delivery

This module provides a unified interface for sending notifications
via SMS, WhatsApp, and Email.

For now, implements console backends that log to database.
In production, integrate with actual providers (Twilio, WhatsApp Business API, SMTP).
"""

import logging

from django.conf import settings
from django.core.mail import send_mail

from .models import NotificationLog

logger = logging.getLogger(__name__)


def get_email_header_html():
    logo_url = getattr(settings, "EMAIL_LOGO_URL", f"{settings.STATIC_URL}img/HH_P_V_Logo(hospital)_.png")
    if not logo_url.startswith("http"):
        logo_url = f"https://{settings.ALLOWED_HOSTS[0]}{logo_url}" if getattr(settings, "ALLOWED_HOSTS", []) else logo_url
    return f"""<div style="padding: 20px 30px; background-color: #ffffff;">
        <table width="100%" cellpadding="0" cellspacing="0" border="0">
            <tr>
                <td style="text-align: left; width: 90px; vertical-align: middle;">
                    <img src="{logo_url}" alt="Al Hammadi" width="75" style="display: block; max-width: 75px; height: auto;">
                </td>
                <td style="text-align: right; direction: rtl; font-size: 15px; color: #005696; font-weight: 700; vertical-align: middle; font-family: 'Segoe UI', Tahoma, sans-serif;">
                    مستشفى الحمادي
                </td>
            </tr>
        </table>
    </div>
    <div style="height: 2px; background-color: #005696; font-size: 1px; line-height: 1px;">&nbsp;</div>"""


class NotificationService:
    """
    Unified notification service for all channels.

    Usage:
        NotificationService.send_sms('+966501234567', 'Your survey is ready')
        NotificationService.send_email('user@email.com', 'Survey', 'Please complete...')
    """

    @staticmethod
    def send_sms(phone, message, related_object=None, metadata=None):
        """
        Send SMS notification.

        Routing order:
        1. External SMS API (if SMS_API_ENABLED)
        2. Mshastra (if SMS_PROVIDER=mshastra and credentials configured)
        3. Twilio (if SMS_PROVIDER=twilio and credentials configured)
        4. Console logger (fallback)

        Args:
            phone: Recipient phone number
            message: SMS message text
            related_object: Related model instance (optional)
            metadata: Additional metadata dict (optional)

        Returns:
            NotificationLog instance
        """
        dev_recipient = getattr(settings, "DEV_SMS_RECIPIENT", "")
        if settings.DEBUG and dev_recipient:
            original_phone = phone
            phone = dev_recipient
            logger.info(f"[DEV SMS Redirect] Original: {original_phone} -> Routed to: {phone}")

        sms_api_config = settings.EXTERNAL_NOTIFICATION_API.get("sms", {})
        if sms_api_config.get("enabled", False):
            return NotificationService.send_sms_via_api(
                message=message, phone=phone, related_object=related_object, metadata=metadata
            )

        sms_config = settings.NOTIFICATION_CHANNELS.get("sms", {})
        provider = sms_config.get("provider", "console")

        log = NotificationLog.objects.create(
            channel="sms",
            recipient=phone,
            message=message,
            content_object=related_object,
            provider=provider,
            metadata=metadata or {},
        )

        if provider == "mshastra":
            return NotificationService._send_sms_mshastra(log, phone, message)

        if provider == "twilio":
            return NotificationService._send_sms_twilio(log, phone, message)

        logger.info(f"[SMS Console] To: {phone} | Message: {message}")
        log.mark_sent()
        return log

    @staticmethod
    def _send_sms_twilio(log, phone, message):
        """
        Send SMS via Twilio SDK.

        Requires: TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and
        either TWILIO_PHONE_NUMBER or TWILIO_MESSAGING_SERVICE_SID.
        """
        from twilio.base.exceptions import TwilioRestException
        from twilio.rest import Client

        account_sid = settings.TWILIO_ACCOUNT_SID
        auth_token = settings.TWILIO_AUTH_TOKEN

        if not account_sid or not auth_token:
            logger.warning("Twilio credentials not configured, falling back to console")
            log.provider = "console"
            log.save(update_fields=["provider"])
            logger.info(f"[SMS Console] To: {phone} | Message: {message}")
            log.mark_sent()
            return log

        try:
            client = Client(account_sid, auth_token)

            twilio_kwargs = {
                "body": message,
                "to": phone,
            }

            if settings.TWILIO_MESSAGING_SERVICE_SID:
                twilio_kwargs["messaging_service_sid"] = settings.TWILIO_MESSAGING_SERVICE_SID
            elif settings.TWILIO_PHONE_NUMBER:
                twilio_kwargs["from_"] = settings.TWILIO_PHONE_NUMBER
            else:
                logger.warning("Twilio phone number or messaging service SID not configured, falling back to console")
                log.provider = "console"
                log.save(update_fields=["provider"])
                logger.info(f"[SMS Console] To: {phone} | Message: {message}")
                log.mark_sent()
                return log

            twilio_message = client.messages.create(**twilio_kwargs)

            log.mark_sent(provider_message_id=twilio_message.sid)
            log.provider_response = {
                "sid": twilio_message.sid,
                "status": twilio_message.status,
                "direction": twilio_message.direction,
                "to": twilio_message.to,
            }
            log.save(update_fields=["provider_response"])

            logger.info(f"SMS sent via Twilio to {phone}: sid={twilio_message.sid}")
            return log

        except TwilioRestException as e:
            logger.error(f"Twilio error sending SMS to {phone}: {e}")
            log.mark_failed(str(e))
            return log

        except Exception as e:
            logger.error(f"Unexpected error sending SMS via Twilio to {phone}: {e}", exc_info=True)
            log.mark_failed(str(e))
            return log

    @staticmethod
    def _send_sms_mshastra(log, phone, message):
        """
        Send SMS via Mshastra API.

        Requires: MSHASTRA_USERNAME, MSHASTRA_PASSWORD, and MSHASTRA_SENDER_ID.
        API: https://mshastra.com/sendurl.aspx
        """
        import requests

        username = settings.MSHASTRA_USERNAME
        password = settings.MSHASTRA_PASSWORD
        sender_id = settings.MSHASTRA_SENDER_ID

        if not username or not password:
            logger.warning("Mshastra credentials not configured, falling back to console")
            log.provider = "console"
            log.save(update_fields=["provider"])
            logger.info(f"[SMS Console] To: {phone} | Message: {message}")
            log.mark_sent()
            return log

        try:
            url = "https://mshastra.com/sendurl.aspx"
            params = {
                "user": username,
                "pwd": password,
                "senderid": sender_id,
                "mobileno": phone,
                "msgtext": message,
                "priority": "High",
                "CountryCode": "ALL",
            }

            response = requests.get(url, params=params, timeout=30)
            logger.info(f"Mshastra API response for {phone}: {response.text}")
            response_text = response.text.strip()

            log.provider_response = {"status_code": response.status_code, "response": response_text}
            log.save(update_fields=["provider_response"])

            if "Send Successful" in response_text:
                log.mark_sent()
                logger.info(f"SMS sent via Mshastra to {phone}: {response_text}")
            else:
                log.mark_failed(response_text)
                logger.warning(f"Mshastra SMS failed for {phone}: {response_text}")

            return log

        except requests.exceptions.Timeout:
            logger.error(f"Mshastra API timeout for {phone}")
            log.mark_failed("Request timeout")
            return log

        except requests.exceptions.ConnectionError:
            logger.error(f"Mshastra API connection error for {phone}")
            log.mark_failed("Connection error")
            return log

        except Exception as e:
            logger.error(f"Unexpected error sending SMS via Mshastra to {phone}: {e}", exc_info=True)
            log.mark_failed(str(e))
            return log

    @staticmethod
    def send_whatsapp(phone, message, related_object=None, metadata=None):
        """
        Send WhatsApp notification.

        Args:
            phone: Recipient phone number
            message: WhatsApp message text
            related_object: Related model instance (optional)
            metadata: Additional metadata dict (optional)

        Returns:
            NotificationLog instance
        """
        # Create notification log
        log = NotificationLog.objects.create(
            channel="whatsapp",
            recipient=phone,
            message=message,
            content_object=related_object,
            provider="console",  # TODO: Replace with actual provider
            metadata=metadata or {},
        )

        # Check if WhatsApp is enabled
        whatsapp_config = settings.NOTIFICATION_CHANNELS.get("whatsapp", {})
        if not whatsapp_config.get("enabled", False):
            logger.info(f"[WhatsApp Console] To: {phone} | Message: {message}")
            log.mark_sent()
            return log

        # TODO: Integrate with WhatsApp Business API
        # Example:
        # try:
        #     response = requests.post(
        #         f"{settings.WHATSAPP_API_URL}/messages",
        #         headers={'Authorization': f'Bearer {settings.WHATSAPP_API_KEY}'},
        #         json={
        #             'to': phone,
        #             'type': 'text',
        #             'text': {'body': message}
        #         }
        #     )
        #     log.mark_sent(provider_message_id=response.json().get('id'))
        # except Exception as e:
        #     log.mark_failed(str(e))

        # Console backend for now
        logger.info(f"[WhatsApp Console] To: {phone} | Message: {message}")
        log.mark_sent()

        return log

    @staticmethod
    def send_email(
        email,
        subject,
        message,
        html_message=None,
        related_object=None,
        metadata=None,
        notification_type="system",
        user=None,
    ):
        """
        Send Email notification and create corresponding in-app notification.

        Args:
            email: Recipient email address
            subject: Email subject
            message: Email message (plain text)
            html_message: Email message (HTML) (optional)
            related_object: Related model instance (optional)
            metadata: Additional metadata dict (optional)
            notification_type: Type of notification (default: 'system')
            user: User instance to create in-app notification for (optional, will try to lookup by email)

        Returns:
            NotificationLog instance
        """
        from apps.accounts.models import User

        # Check if Email API is enabled and use it (simulator or external API)
        email_api_config = settings.EXTERNAL_NOTIFICATION_API.get("email", {})
        if email_api_config.get("enabled", False):
            log = NotificationService.send_email_via_api(
                message=message,
                email=email,
                subject=subject,
                html_message=html_message,
                related_object=related_object,
                metadata=metadata,
            )
            # Create in-app notification after sending via API
            NotificationService._create_in_app_notification_from_email(
                log, email, subject, message, notification_type, related_object, user
            )
            return log

        # Create notification log
        log = NotificationLog.objects.create(
            channel="email",
            recipient=email,
            subject=subject,
            message=message,
            content_object=related_object,
            provider="console",  # TODO: Replace with actual provider
            metadata=metadata or {},
        )

        # Create in-app notification
        NotificationService._create_in_app_notification_from_email(
            log, email, subject, message, notification_type, related_object, user
        )

        # Check if Email is enabled
        email_config = settings.NOTIFICATION_CHANNELS.get("email", {})
        if not email_config.get("enabled", True):
            logger.info(f"[Email Console] To: {email} | Subject: {subject} | Message: {message}")
            log.mark_sent()
            return log

        # Send email using Django's email backend
        try:
            send_mail(
                subject=subject,
                message=message,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[email],
                html_message=html_message,
                fail_silently=False,
            )
            log.mark_sent()
            logger.info(f"Email sent to {email}: {subject}")
        except Exception as e:
            log.mark_failed(str(e))
            logger.error(f"Failed to send email to {email}: {str(e)}")

        return log

    @staticmethod
    def _create_in_app_notification_from_email(
        log, email, subject, message, notification_type, related_object=None, user=None
    ):
        """
        Create in-app notification from email data.
        Private helper method.
        """
        from apps.accounts.models import User
        from .models import UserNotification

        # Try to find user if not provided
        if not user:
            try:
                user = User.objects.get(email=email)
            except User.DoesNotExist:
                # User not found, skip in-app notification
                return None

        # Create in-app notification
        notification = UserNotification.objects.create(
            user=user,
            title=subject,
            message=message[:500],  # Truncate for preview
            notification_type=notification_type,
            email_log=log,
            content_object=related_object,
        )

        logger.info(f"In-app notification created for {user.email}: {subject}")
        return notification

    @staticmethod
    def send_email_via_api(message, email, subject, html_message=None, related_object=None, metadata=None):
        """
        Send email via external API endpoint with retry logic.

        Args:
            message: Email message (plain text)
            email: Recipient email address
            subject: Email subject
            html_message: Email message (HTML) (optional)
            related_object: Related model instance (optional)
            metadata: Additional metadata dict (optional)

        Returns:
            NotificationLog instance
        """
        import requests
        import time

        # Check if enabled
        email_config = settings.EXTERNAL_NOTIFICATION_API.get("email", {})
        if not email_config.get("enabled", False):
            logger.warning("Email API is disabled. Skipping send_email_via_api")
            return None

        # Create notification log
        log = NotificationLog.objects.create(
            channel="email",
            recipient=email,
            subject=subject,
            message=message,
            content_object=related_object,
            provider="api",
            metadata={
                "api_url": email_config.get("url"),
                "auth_method": email_config.get("auth_method"),
                **(metadata or {}),
            },
        )

        # Prepare request payload
        payload = {
            "to": email,
            "subject": subject,
            "message": message,
        }
        if html_message:
            payload["html_message"] = html_message

        # Prepare headers
        headers = {"Content-Type": "application/json"}
        api_key = email_config.get("api_key", "")
        auth_method = email_config.get("auth_method", "bearer")

        if auth_method == "bearer":
            headers["Authorization"] = f"Bearer {api_key}"
        elif auth_method == "api_key":
            headers["X-API-KEY"] = api_key

        # Retry logic
        max_retries = email_config.get("max_retries", 3)
        retry_delay = email_config.get("retry_delay", 2)
        timeout = email_config.get("timeout", 10)

        for attempt in range(max_retries):
            try:
                logger.info(f"Sending email via API (attempt {attempt + 1}/{max_retries}) to {email}")

                response = requests.post(email_config.get("url"), json=payload, headers=headers, timeout=timeout)

                # API runs in background, accept any 2xx response
                if 200 <= response.status_code < 300:
                    log.mark_sent()
                    logger.info(f"Email sent via API to {email}: {subject}")
                    return log
                else:
                    logger.warning(f"API returned status {response.status_code}")
                    if attempt == max_retries - 1:
                        log.mark_failed(f"API returned status {response.status_code}")
                    continue

            except requests.exceptions.Timeout:
                logger.warning(f"Timeout on attempt {attempt + 1}")
                if attempt == max_retries - 1:
                    log.mark_failed("Request timeout")

            except requests.exceptions.ConnectionError:
                logger.warning(f"Connection error on attempt {attempt + 1}")
                if attempt == max_retries - 1:
                    log.mark_failed("Connection error")

            except Exception as e:
                logger.error(f"Unexpected error: {str(e)}")
                if attempt == max_retries - 1:
                    log.mark_failed(str(e))

            # Wait before retry (exponential backoff)
            if attempt < max_retries - 1:
                time.sleep(retry_delay * (2**attempt))

        return log

    @staticmethod
    def send_sms_via_api(message, phone, related_object=None, metadata=None):
        """
        Send SMS via external API endpoint with retry logic.

        Args:
            message: SMS message text
            phone: Recipient phone number
            related_object: Related model instance (optional)
            metadata: Additional metadata dict (optional)

        Returns:
            NotificationLog instance
        """
        import requests
        import time

        # Check if enabled
        sms_config = settings.EXTERNAL_NOTIFICATION_API.get("sms", {})
        if not sms_config.get("enabled", False):
            logger.warning("SMS API is disabled. Skipping send_sms_via_api")
            return None

        # Create notification log
        log = NotificationLog.objects.create(
            channel="sms",
            recipient=phone,
            message=message,
            content_object=related_object,
            provider="api",
            metadata={
                "api_url": sms_config.get("url"),
                "auth_method": sms_config.get("auth_method"),
                **(metadata or {}),
            },
        )

        # Prepare request payload
        payload = {
            "to": phone,
            "message": message,
        }

        # Prepare headers
        headers = {"Content-Type": "application/json"}
        api_key = sms_config.get("api_key", "")
        auth_method = sms_config.get("auth_method", "bearer")

        if auth_method == "bearer":
            headers["Authorization"] = f"Bearer {api_key}"
        elif auth_method == "api_key":
            headers["X-API-KEY"] = api_key

        # Retry logic
        max_retries = sms_config.get("max_retries", 3)
        retry_delay = sms_config.get("retry_delay", 2)
        timeout = sms_config.get("timeout", 10)

        for attempt in range(max_retries):
            try:
                logger.info(f"Sending SMS via API (attempt {attempt + 1}/{max_retries}) to {phone}")

                response = requests.post(sms_config.get("url"), json=payload, headers=headers, timeout=timeout)

                # API runs in background, accept any 2xx response
                if 200 <= response.status_code < 300:
                    log.mark_sent()
                    logger.info(f"SMS sent via API to {phone}")
                    return log
                else:
                    logger.warning(f"API returned status {response.status_code}")
                    if attempt == max_retries - 1:
                        log.mark_failed(f"API returned status {response.status_code}")
                    continue

            except requests.exceptions.Timeout:
                logger.warning(f"Timeout on attempt {attempt + 1}")
                if attempt == max_retries - 1:
                    log.mark_failed("Request timeout")

            except requests.exceptions.ConnectionError:
                logger.warning(f"Connection error on attempt {attempt + 1}")
                if attempt == max_retries - 1:
                    log.mark_failed("Connection error")

            except Exception as e:
                logger.error(f"Unexpected error: {str(e)}")
                if attempt == max_retries - 1:
                    log.mark_failed(str(e))

            # Wait before retry (exponential backoff)
            if attempt < max_retries - 1:
                time.sleep(retry_delay * (2**attempt))

        return log

    @staticmethod
    def send_notification(recipient, title, message, notification_type="general", related_object=None, metadata=None):
        """
        Send generic notification to a user.

        This method determines the best channel to use based on recipient preferences
        or defaults to email.

        Args:
            recipient: User object
            title: Notification title
            message: Notification message
            notification_type: Type of notification (e.g., 'complaint', 'survey', 'general')
            related_object: Related model instance (optional)
            metadata: Additional metadata dict (optional)

        Returns:
            NotificationLog instance
        """
        # Determine the recipient's contact information
        recipient_email = recipient.email if hasattr(recipient, "email") else None
        recipient_phone = recipient.phone if hasattr(recipient, "phone") else None

        # Try email first (most reliable)
        if recipient_email:
            try:
                return NotificationService.send_email(
                    email=recipient_email,
                    subject=title,
                    message=message,
                    related_object=related_object,
                    metadata={"notification_type": notification_type, **(metadata or {})},
                )
            except Exception as e:
                logger.warning(f"Failed to send email notification to {recipient_email}: {str(e)}")

        # Fallback to SMS if email failed or not available
        if recipient_phone:
            try:
                # Combine title and message for SMS
                sms_message = f"{title}\n\n{message}"
                return NotificationService.send_sms(
                    phone=recipient_phone,
                    message=sms_message,
                    related_object=related_object,
                    metadata={"notification_type": notification_type, **(metadata or {})},
                )
            except Exception as e:
                logger.warning(f"Failed to send SMS notification to {recipient_phone}: {str(e)}")

        # If all channels failed, log a console notification
        logger.warning(
            f"Could not send notification to {recipient}. "
            f"No valid contact channels available. "
            f"Title: {title}, Message: {message}"
        )

        # Create a log entry even if we couldn't send
        return NotificationLog.objects.create(
            channel="console",
            recipient=str(recipient),
            subject=title,
            message=message,
            content_object=related_object,
            provider="console",
            metadata={"notification_type": notification_type, **(metadata or {})},
        )

    @staticmethod
    def send_survey_invitation(survey_instance, language="en"):
        """
        Send survey invitation to patient.

        Args:
            survey_instance: SurveyInstance object
            language: Language code ('en' or 'ar')

        Returns:
            NotificationLog instance
        """
        patient = survey_instance.patient
        survey_url = survey_instance.get_survey_url()

        # Determine recipient based on delivery channel
        if survey_instance.delivery_channel == "sms":
            recipient = survey_instance.recipient_phone or patient.phone
            if language == "ar":
                message = f"عزيزي {patient.get_full_name()},\n\nنرجو منك إكمال استبيان تجربتك:\n{survey_url}"
            else:
                message = f"Dear {patient.get_full_name()},\n\nPlease complete your experience survey:\n{survey_url}"

            return NotificationService.send_sms(
                phone=recipient,
                message=message,
                related_object=survey_instance,
                metadata={"survey_id": str(survey_instance.id), "language": language},
            )

        elif survey_instance.delivery_channel == "whatsapp":
            recipient = survey_instance.recipient_phone or patient.phone
            if language == "ar":
                message = f"عزيزي {patient.get_full_name()},\n\nنرجو منك إكمال استبيان تجربتك:\n{survey_url}"
            else:
                message = f"Dear {patient.get_full_name()},\n\nPlease complete your experience survey:\n{survey_url}"

            return NotificationService.send_whatsapp(
                phone=recipient,
                message=message,
                related_object=survey_instance,
                metadata={"survey_id": str(survey_instance.id), "language": language},
            )

        else:  # email
            recipient = survey_instance.recipient_email or patient.email
            survey_label = (
                survey_instance.survey_template.name_ar or survey_instance.survey_template.name
                if survey_instance.survey_template
                else survey_instance.metadata.get("patient_type", "Patient Experience Survey")
            )
            if language == "ar":
                subject = f"استبيان تجربتك - {survey_label}"
                message = f"عزيزي {patient.get_full_name()},\n\nنرجو منك إكمال استبيان تجربتك:\n{survey_url}"
            else:
                subject = f"Your Experience Survey - {survey_label}"
                message = f"Dear {patient.get_full_name()},\n\nPlease complete your experience survey:\n{survey_url}"

            return NotificationService.send_email(
                email=recipient,
                subject=subject,
                message=message,
                related_object=survey_instance,
                metadata={"survey_id": str(survey_instance.id), "language": language},
            )


# Convenience functions
def send_sms(phone, message, **kwargs):
    """Send SMS notification"""
    return NotificationService.send_sms(phone, message, **kwargs)


def send_whatsapp(phone, message, **kwargs):
    """Send WhatsApp notification"""
    return NotificationService.send_whatsapp(phone, message, **kwargs)


def send_email(email, subject, message, **kwargs):
    """Send Email notification"""
    return NotificationService.send_email(email, subject, message, **kwargs)


def send_notification(recipient, title, message, **kwargs):
    """Send generic notification to a user"""
    return NotificationService.send_notification(recipient, title, message, **kwargs)


def create_in_app_notification(
    user,
    title,
    message,
    notification_type="system",
    title_ar="",
    message_ar="",
    related_object=None,
    action_url="",
    email_log=None,
):
    """
    Create an in-app notification for a user.

    This is automatically called when sending emails to create
    corresponding in-app notifications.

    Args:
        user: User instance to notify
        title: Notification title (EN)
        message: Notification message (EN)
        notification_type: Type of notification (e.g., 'complaint_assigned')
        title_ar: Arabic title (optional)
        message_ar: Arabic message (optional)
        related_object: Related model instance (optional)
        action_url: URL to navigate when clicked (optional)
        email_log: Associated NotificationLog instance (optional)

    Returns:
        UserNotification instance
    """
    from .models import UserNotification

    notification = UserNotification.objects.create(
        user=user,
        title=title,
        title_ar=title_ar or title,
        message=message,
        message_ar=message_ar or message,
        notification_type=notification_type,
        action_url=action_url,
        email_log=email_log,
    )

    # Set related object if provided
    if related_object:
        notification.content_object = related_object
        notification.save(update_fields=["content_type", "object_id"])

    return notification
