"""
SendGrid email notification provider.

Sends transactional email via the SendGrid Web API v3 using httpx (no SDK).
If SENDGRID_API_KEY is empty the send is skipped and the message is logged
instead — matching the stub behaviour of GmailProvider.
"""

import logging
import re
from typing import Any, Dict, Optional

import httpx

from src.apps.notifications.providers.base import NotificationProvider
from src.core.config import settings

logger = logging.getLogger(__name__)

SENDGRID_MAIL_SEND_URL = "https://api.sendgrid.com/v3/mail/send"

EMAIL_REGEX = re.compile(r"^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$")


class SendGridProvider(NotificationProvider):
    """
    Sends transactional email via the SendGrid Web API v3.

    Configuration (settings):
      SENDGRID_API_KEY      — SendGrid API key; empty = stub mode.
      SENDGRID_FROM_EMAIL   — From address for outgoing emails.
      SENDGRID_FROM_NAME    — Display name shown in the From header.
    """

    def _validate_recipient(self, recipient: str) -> None:
        """Validate email address format and prevent header injection."""
        if not recipient or not EMAIL_REGEX.fullmatch(recipient.strip()):
            raise ValueError("Invalid recipient email address")
        if any(c in recipient for c in ['\r', '\n', '\0']):
            raise ValueError("Recipient contains invalid characters")

    async def send(
        self,
        recipient: str,
        subject: Optional[str],
        body_text: Optional[str],
        body_html: Optional[str],
        template_vars: Optional[Dict[str, Any]] = None,
    ) -> bool:
        self._validate_recipient(recipient)

        api_key = settings.SENDGRID_API_KEY
        from_email = settings.SENDGRID_FROM_EMAIL
        from_name = settings.SENDGRID_FROM_NAME

        if not api_key:
            masked_to = (
                f"{'*' * (len(recipient) - 4)}{recipient[-4:]}"
                if len(recipient) > 4
                else "****"
            )
            logger.info(
                "[EMAIL STUB] Would send via SendGrid to %s | Subject: %s",
                masked_to,
                subject,
            )
            return True

        content = []
        if body_text:
            content.append({"type": "text/plain", "value": body_text})
        if body_html:
            content.append({"type": "text/html", "value": body_html})

        if not content:
            content.append({"type": "text/plain", "value": ""})

        payload = {
            "personalizations": [
                {"to": [{"email": recipient}]},
            ],
            "from": {"email": from_email, "name": from_name},
            "subject": subject or "(no subject)",
            "content": content,
        }

        headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        }

        try:
            async with httpx.AsyncClient(timeout=30.0) as client:
                response = await client.post(
                    SENDGRID_MAIL_SEND_URL,
                    json=payload,
                    headers=headers,
                )
            # SendGrid returns 202 Accepted on success.
            if response.status_code in (200, 202):
                logger.info("Email sent successfully via SendGrid to %s", recipient)
                return True

            try:
                error_detail = response.json().get("errors", [{}])[0].get("message", "unknown")
            except Exception:
                error_detail = f"HTTP {response.status_code}"
            logger.error("SendGrid API error %s: %s", response.status_code, error_detail)
            return False

        except Exception as exc:
            logger.error("SendGrid send failed: %s", exc)
            return False
