"""
Core notification service using Apprise.
"""

import re
from typing import Dict, List, Optional, Any
from urllib.parse import urlparse

import apprise
from loguru import logger
from tenacity import (
    retry,
    stop_after_attempt,
    wait_exponential,
    retry_if_exception_type,
)

from .exceptions import ServiceError, SendError
from .templates import EventType, NotificationTemplate
from ..security.url_builder import mask_sensitive_url
from ..security.notification_validator import (
    NotificationURLValidator,
)

# Backward compatibility constants - now handled by Tenacity internally
MAX_RETRY_ATTEMPTS = 3
INITIAL_RETRY_DELAY = 0.5
RETRY_BACKOFF_MULTIPLIER = 2


class NotificationService:
    """
    Low-level notification service that wraps Apprise.
    """

    # Regex patterns for common service types (for validation)
    SERVICE_PATTERNS = {
        "email": r"^mailto://",
        "discord": r"^discord://",
        "slack": r"^slack://",
        "telegram": r"^tgram://",
        "smtp": r"^(smtp|smtps)://",
    }

    def __init__(self, allow_private_ips: bool = False):
        """
        Initialize the notification service.

        Args:
            allow_private_ips: Whether to allow notifications to private/local IPs
                              (default: False for security). Set to True for
                              development/testing environments only.
        """
        self.apprise = apprise.Apprise()
        self.allow_private_ips = allow_private_ips

    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=0.5, max=10),
        retry=retry_if_exception_type((Exception,)),
        reraise=True,
    )
    def _send_with_retry(
        self,
        title: str,
        body: str,
        apprise_instance: apprise.Apprise,
        tag: Optional[str] = None,
        attach: Optional[List[str]] = None,
    ) -> bool:
        """
        Send a notification using the provided Apprise instance with retry logic.

        This method is decorated with Tenacity to handle retries automatically.

        Args:
            title: Notification title
            body: Notification body text
            apprise_instance: Apprise instance to use for sending
            tag: Optional tag to target specific services
            attach: Optional list of file paths to attach

        Returns:
            True if notification was sent successfully

        Raises:
            SendError: If sending fails after all retry attempts
        """
        logger.debug(
            f"Sending notification: title='{title[:50]}...', tag={tag}"
        )
        logger.debug(f"Body preview: {body[:200]}...")

        # Send notification
        notify_result = apprise_instance.notify(
            title=title,
            body=body,
            tag=tag,
            attach=attach,
        )

        if notify_result:
            logger.debug(f"Notification sent successfully: '{title[:50]}...'")
            return True
        else:
            error_msg = "Failed to send notification to any service"
            logger.warning(error_msg)
            raise SendError(error_msg)

    def send(
        self,
        title: str,
        body: str,
        service_urls: Optional[str] = None,
        tag: Optional[str] = None,
        attach: Optional[List[str]] = None,
    ) -> bool:
        """
        Send a notification to service URLs with automatic retry.

        Args:
            title: Notification title
            body: Notification body text
            service_urls: Comma-separated list of service URLs to override configured ones
            tag: Optional tag to target specific services
            attach: Optional list of file paths to attach

        Returns:
            True if notification was sent successfully to at least one service

        Raises:
            SendError: If sending fails after all retry attempts

        Note:
            Temporary Apprise instances are created for each send operation
            and are automatically garbage collected by Python when they go
            out of scope. This simple approach is ideal for small deployments
            (~5 users) and avoids memory management complexity.
        """
        try:
            # If service_urls are provided, create a new Apprise instance
            if service_urls:
                logger.debug(
                    "Creating Apprise instance for provided service URLs"
                )

                # Validate service URLs for security (SSRF prevention)
                is_valid, error_msg = (
                    NotificationURLValidator.validate_multiple_urls(
                        service_urls, allow_private_ips=self.allow_private_ips
                    )
                )

                if not is_valid:
                    logger.error(
                        f"Service URL validation failed: {error_msg}. "
                        f"URL: {mask_sensitive_url(service_urls)}"
                    )
                    raise ServiceError(f"Invalid service URL: {error_msg}")

                temp_apprise = apprise.Apprise()
                result = temp_apprise.add(service_urls, tag=tag)

                if not result:
                    logger.error(
                        f"Failed to add service URLs to Apprise: "
                        f"{mask_sensitive_url(service_urls)}"
                    )
                    return False

                # Send notification with the temp instance (with retry)
                return self._send_with_retry(
                    title, body, temp_apprise, tag, attach
                )
            else:
                # Use the configured apprise instance
                if len(self.apprise) == 0:
                    logger.debug(
                        "No notification services configured in Apprise"
                    )
                    return False

                # Send notification (with retry)
                return self._send_with_retry(
                    title, body, self.apprise, tag, attach
                )

        except Exception as e:
            # Tenacity will retry, but if all retries fail, raise SendError
            logger.exception(
                f"Failed to send notification after retries: '{title[:50]}...'"
            )
            raise SendError(f"Failed to send notification: {str(e)}")

    def send_event(
        self,
        event_type: EventType,
        context: Dict[str, Any],
        service_urls: Optional[str] = None,
        tag: Optional[str] = None,
        custom_template: Optional[Dict[str, str]] = None,
    ) -> bool:
        """
        Send a notification for a specific event type.

        Args:
            event_type: Type of event
            context: Context data for template formatting
            service_urls: Comma-separated list of service URLs
            tag: Optional tag to target specific services
            custom_template: Optional custom template override

        Returns:
            True if notification was sent successfully
        """
        logger.debug(f"send_event: event_type={event_type.value}, tag={tag}")
        logger.debug(f"Context: {context}")

        # Format notification using template
        message = NotificationTemplate.format(
            event_type, context, custom_template
        )
        logger.debug(
            f"Template formatted - title: '{message['title'][:50]}...'"
        )

        # Send notification
        result = self.send(
            title=message["title"],
            body=message["body"],
            service_urls=service_urls,
            tag=tag,
        )
        return result

    def test_service(self, url: str) -> Dict[str, Any]:
        """
        Test a notification service.

        Args:
            url: Apprise-compatible service URL

        Returns:
            Dict with 'success' boolean and optional 'error' message
        """
        try:
            # Validate service URL for security (SSRF prevention)
            is_valid, error_msg = NotificationURLValidator.validate_service_url(
                url, allow_private_ips=self.allow_private_ips
            )

            if not is_valid:
                logger.warning(
                    f"Test service URL validation failed: {error_msg}. "
                    f"URL: {mask_sensitive_url(url)}"
                )
                return {
                    "success": False,
                    "error": "Invalid notification service URL.",
                }

            # Create temporary Apprise instance
            temp_apprise = apprise.Apprise()
            add_result = temp_apprise.add(url)

            if not add_result:
                return {
                    "success": False,
                    "error": "Failed to add service URL",
                }

            # Send test notification
            result = temp_apprise.notify(
                title="Test Notification",
                body=(
                    "This is a test notification from Local Deep Research. "
                    "If you see this, your service is configured correctly!"
                ),
            )

            if result:
                return {
                    "success": True,
                    "message": "Test notification sent successfully",
                }
            else:
                return {
                    "success": False,
                    "error": "Failed to send test notification",
                }

        except Exception:
            logger.exception("Error testing notification service")
            return {
                "success": False,
                "error": "Failed to test notification service.",
            }

    @staticmethod
    def _validate_url(url: str) -> None:
        """
        Validate a notification service URL.

        Args:
            url: URL to validate

        Raises:
            ServiceError: If URL is invalid

        Note:
            URL scheme validation is handled by Apprise itself, which maintains
            a comprehensive whitelist of supported notification services.
            Apprise will reject unsupported schemes like 'file://' or 'javascript://'.
            See: https://github.com/caronc/apprise/wiki
        """
        if not url or not isinstance(url, str):
            raise ServiceError("URL must be a non-empty string")

        # Check if it looks like a URL
        parsed = urlparse(url)
        if not parsed.scheme:
            raise ServiceError(
                "Invalid URL format. Must be an Apprise-compatible "
                "service URL (e.g., discord://webhook_id/token)"
            )

    def get_service_type(self, url: str) -> Optional[str]:
        """
        Detect service type from URL.

        Args:
            url: Service URL

        Returns:
            Service type name or None if unknown
        """
        for service_name, pattern in self.SERVICE_PATTERNS.items():
            if re.match(pattern, url, re.IGNORECASE):
                return service_name
        return "unknown"
