"""Email provider interfaces and factory.""" import logging import os from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Optional logger = logging.getLogger(__name__) @dataclass class EmailMessage: """Email message data structure.""" to: str subject: str body: str html_body: Optional[str] = None from_address: Optional[str] = None class EmailProvider(ABC): """Abstract base class for email providers.""" @abstractmethod def send(self, message: EmailMessage) -> bool: """ Send an email message. Args: message: EmailMessage instance containing email details Returns: bool: True if email was sent successfully, False otherwise """ pass class NoOpEmailProvider(EmailProvider): """No-op email provider that logs and returns False.""" def send(self, message: EmailMessage) -> bool: """Log that emails are disabled and return False.""" logger.info(f"Email disabled - would send to={message.to} subject={message.subject}") return False class EmailProviderFactory: """Factory for creating email provider instances.""" @staticmethod def get_provider() -> EmailProvider: """ Create an email provider based on EMAIL_PROVIDER config. Returns: EmailProvider: An instance of the appropriate email provider """ provider_name = os.getenv("EMAIL_PROVIDER", "smtp").lower() if provider_name == "smtp": try: from gatehouse_app.services.providers.smtp_provider import SmtpEmailProvider return SmtpEmailProvider() except ImportError: logger.warning("SMTP provider not implemented, using no-op provider") return NoOpEmailProvider() if provider_name == "mailgun": try: from gatehouse_app.services.providers.mailgun_provider import MailgunEmailProvider return MailgunEmailProvider() except ImportError: logger.warning("Mailgun provider not implemented, using no-op provider") return NoOpEmailProvider() if provider_name == "sendgrid": try: from gatehouse_app.services.providers.sendgrid_provider import SendGridEmailProvider return SendGridEmailProvider() except ImportError: logger.warning("SendGrid provider not implemented, using no-op provider") return NoOpEmailProvider() logger.error(f"Invalid EMAIL_PROVIDER value: {provider_name}, defaulting to no-op provider") return NoOpEmailProvider()