# ================================================================
# services/notification/app/services/email_service.py
# ================================================================
"""
Email service for sending notifications
Handles SMTP configuration and email delivery
"""

import structlog
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from email.utils import formataddr
from typing import Optional, List, Dict, Any
import aiosmtplib
from jinja2 import Template
import asyncio

from app.core.config import settings
from shared.monitoring.metrics import MetricsCollector

logger = structlog.get_logger()
metrics = MetricsCollector("notification-service")

class EmailService:
    """
    Email service for sending notifications via SMTP
    Supports both plain text and HTML emails
    """
    
    def __init__(self):
        self.smtp_host = settings.SMTP_HOST
        self.smtp_port = settings.SMTP_PORT
        self.smtp_user = settings.SMTP_USER
        self.smtp_password = settings.SMTP_PASSWORD
        self.smtp_tls = settings.SMTP_TLS
        self.smtp_ssl = settings.SMTP_SSL
        self.smtp_require_auth = settings.SMTP_REQUIRE_AUTH
        self.default_from_email = settings.DEFAULT_FROM_EMAIL
        self.default_from_name = settings.DEFAULT_FROM_NAME
        
        # Log SMTP configuration mode
        if not self.smtp_require_auth or (not self.smtp_user and not self.smtp_password):
            logger.info("Email service configured for trusted relay mode (no authentication)")
        else:
            logger.info("Email service configured with SMTP authentication")
    
    async def send_email(
        self,
        to_email: str,
        subject: str,
        text_content: str,
        html_content: Optional[str] = None,
        from_email: Optional[str] = None,
        from_name: Optional[str] = None,
        reply_to: Optional[str] = None,
        attachments: Optional[List[Dict[str, Any]]] = None
    ) -> bool:
        """
        Send an email notification
        
        Args:
            to_email: Recipient email address
            subject: Email subject
            text_content: Plain text content
            html_content: HTML content (optional)
            from_email: Sender email (optional, uses default)
            from_name: Sender name (optional, uses default)
            reply_to: Reply-to address (optional)
            attachments: List of attachments (optional)
        
        Returns:
            bool: True if email was sent successfully
        """
        try:
            if not settings.ENABLE_EMAIL_NOTIFICATIONS:
                logger.info("Email notifications disabled")
                return True  # Return success to avoid blocking workflow
            
            # Note: Authentication is optional for trusted relay (e.g., internal Mailu with subnet trust)
            
            # Validate email address
            if not to_email or "@" not in to_email:
                logger.error("Invalid recipient email", email=to_email)
                return False
            
            # Create message
            message = MIMEMultipart('alternative')
            message['Subject'] = subject
            message['To'] = to_email
            
            # Set From header
            sender_email = from_email or self.default_from_email
            sender_name = from_name or self.default_from_name
            message['From'] = formataddr((sender_name, sender_email))
            
            # Set Reply-To if provided
            if reply_to:
                message['Reply-To'] = reply_to
            
            # Add text content
            text_part = MIMEText(text_content, 'plain', 'utf-8')
            message.attach(text_part)
            
            # Add HTML content if provided
            if html_content:
                html_part = MIMEText(html_content, 'html', 'utf-8')
                message.attach(html_part)
            
            # Add attachments if provided
            if attachments:
                for attachment in attachments:
                    await self._add_attachment(message, attachment)
            
            # Send email
            await self._send_smtp_email(message, sender_email, to_email)
            
            logger.info("Email sent successfully", 
                       to=to_email, 
                       subject=subject,
                       from_email=sender_email)
            
            # Record success metrics
            metrics.increment_counter("emails_sent_total", labels={"status": "success"})
            
            return True
            
        except Exception as e:
            logger.error("Failed to send email", 
                        to=to_email, 
                        subject=subject, 
                        error=str(e))
            
            # Record failure metrics
            metrics.increment_counter("emails_sent_total", labels={"status": "failed"})
            
            return False
    
    async def send_bulk_emails(
        self,
        recipients: List[str],
        subject: str,
        text_content: str,
        html_content: Optional[str] = None,
        batch_size: int = 50
    ) -> Dict[str, Any]:
        """
        Send bulk emails with rate limiting
        
        Args:
            recipients: List of recipient email addresses
            subject: Email subject
            text_content: Plain text content
            html_content: HTML content (optional)
            batch_size: Number of emails to send per batch
        
        Returns:
            Dict containing success/failure counts
        """
        results = {
            "total": len(recipients),
            "sent": 0,
            "failed": 0,
            "errors": []
        }
        
        try:
            # Process in batches to respect rate limits
            for i in range(0, len(recipients), batch_size):
                batch = recipients[i:i + batch_size]
                
                # Send emails concurrently within batch
                tasks = [
                    self.send_email(
                        to_email=email,
                        subject=subject,
                        text_content=text_content,
                        html_content=html_content
                    )
                    for email in batch
                ]
                
                batch_results = await asyncio.gather(*tasks, return_exceptions=True)
                
                for email, result in zip(batch, batch_results):
                    if isinstance(result, Exception):
                        results["failed"] += 1
                        results["errors"].append({"email": email, "error": str(result)})
                    elif result:
                        results["sent"] += 1
                    else:
                        results["failed"] += 1
                        results["errors"].append({"email": email, "error": "Unknown error"})
                
                # Rate limiting delay between batches
                if i + batch_size < len(recipients):
                    await asyncio.sleep(1.0)  # 1 second delay between batches
            
            logger.info("Bulk email completed", 
                       total=results["total"],
                       sent=results["sent"],
                       failed=results["failed"])
            
            return results
            
        except Exception as e:
            logger.error("Bulk email failed", error=str(e))
            results["errors"].append({"error": str(e)})
            return results
    
    async def send_template_email(
        self,
        to_email: str,
        template_name: str,
        template_data: Dict[str, Any],
        subject_template: Optional[str] = None
    ) -> bool:
        """
        Send email using a template
        
        Args:
            to_email: Recipient email address
            template_name: Name of the email template
            template_data: Data for template rendering
            subject_template: Subject template string (optional)
        
        Returns:
            bool: True if email was sent successfully
        """
        try:
            # Load template (simplified - in production, load from database)
            template_content = await self._load_email_template(template_name)
            if not template_content:
                logger.error("Template not found", template=template_name)
                return False
            
            # Render subject
            subject = template_name.replace("_", " ").title()
            if subject_template:
                subject_tmpl = Template(subject_template)
                subject = subject_tmpl.render(**template_data)
            
            # Render content
            text_template = Template(template_content.get("text", ""))
            text_content = text_template.render(**template_data)
            
            html_content = None
            if template_content.get("html"):
                html_template = Template(template_content["html"])
                html_content = html_template.render(**template_data)
            
            return await self.send_email(
                to_email=to_email,
                subject=subject,
                text_content=text_content,
                html_content=html_content
            )
            
        except Exception as e:
            logger.error("Failed to send template email", 
                        template=template_name, 
                        error=str(e))
            return False
    
    async def health_check(self) -> bool:
        """
        Check if email service is healthy
        
        Returns:
            bool: True if service is healthy
        """
        try:
            if not settings.ENABLE_EMAIL_NOTIFICATIONS:
                return True  # Service is "healthy" if disabled
            
            # Test SMTP connection
            if self.smtp_ssl:
                # Use implicit TLS/SSL connection (port 465 typically)
                server = aiosmtplib.SMTP(hostname=self.smtp_host, port=self.smtp_port, use_tls=True)
                await server.connect()
            else:
                # Use plain connection, optionally upgrade with STARTTLS
                server = aiosmtplib.SMTP(hostname=self.smtp_host, port=self.smtp_port)
                await server.connect()

                if self.smtp_tls:
                    try:
                        await server.starttls()
                    except Exception as starttls_error:
                        if "already using TLS" in str(starttls_error) or "already secure" in str(starttls_error):
                            logger.debug("SMTP connection already secure, skipping STARTTLS")
                        else:
                            raise starttls_error

            # Login only if required by configuration or if credentials are provided
            # This allows for trusted relay mode where authentication is not needed
            if self.smtp_require_auth and self.smtp_user and self.smtp_password:
                await server.login(self.smtp_user, self.smtp_password)
            elif not self.smtp_require_auth:
                logger.debug("Skipping SMTP authentication - trusted relay mode")
            await server.quit()
            
            logger.info("Email service health check passed")
            return True
            
        except Exception as e:
            logger.error("Email service health check failed", error=str(e))
            return False
    
    # ================================================================
    # PRIVATE HELPER METHODS
    # ================================================================
    
    async def _send_smtp_email(self, message: MIMEMultipart, from_email: str, to_email: str):
        """Send email via SMTP"""
        try:
            # Create SMTP connection
            if self.smtp_ssl:
                server = aiosmtplib.SMTP(
                    hostname=self.smtp_host, 
                    port=self.smtp_port, 
                    use_tls=True,
                    timeout=30
                )
            else:
                server = aiosmtplib.SMTP(
                    hostname=self.smtp_host, 
                    port=self.smtp_port,
                    timeout=30
                )
            
            await server.connect()
            
            # Start TLS if required
            if self.smtp_tls and not self.smtp_ssl:
                await server.starttls()
            
            # Login only if required by configuration or if credentials are provided
            # This allows for trusted relay mode where authentication is not needed
            if self.smtp_require_auth and self.smtp_user and self.smtp_password:
                await server.login(self.smtp_user, self.smtp_password)
            elif not self.smtp_require_auth:
                logger.debug("Skipping SMTP authentication - trusted relay mode")
            
            # Send email
            # Note: aiosmtplib send_message doesn't use from_addr/to_addrs parameters
            # The From/To headers are already set in the MIME message
            await server.send_message(message)
            
            # Close connection
            await server.quit()
            
        except Exception as e:
            logger.error("SMTP send failed", error=str(e))
            raise
    
    async def _add_attachment(self, message: MIMEMultipart, attachment: Dict[str, Any]):
        """Add attachment to email message"""
        try:
            filename = attachment.get("filename", "attachment")
            content = attachment.get("content", b"")
            content_type = attachment.get("content_type", "application/octet-stream")
            
            # Create attachment part
            part = MIMEBase(*content_type.split("/"))
            part.set_payload(content)
            encoders.encode_base64(part)
            
            part.add_header(
                'Content-Disposition',
                f'attachment; filename= {filename}'
            )
            
            message.attach(part)
            
        except Exception as e:
            logger.error("Failed to add attachment", filename=attachment.get("filename"), error=str(e))
    
    async def _load_email_template(self, template_name: str) -> Optional[Dict[str, str]]:
        """Load email template from storage"""
        # Simplified template loading - in production, load from database
        templates = {
            "welcome": {
                "text": """
¡Bienvenido a Bakery Forecast, {{user_name}}!

Gracias por registrarte en nuestra plataforma de pronóstico para panaderías.

Tu cuenta ha sido creada exitosamente y ya puedes comenzar a:
- Subir datos de ventas
- Generar pronósticos de demanda
- Optimizar tu producción

Para comenzar, visita: {{dashboard_url}}

Si tienes alguna pregunta, no dudes en contactarnos.

Saludos,
El equipo de Bakery Forecast
                """,
                "html": """
<html>
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
    <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 20px; text-align: center;">
        <h1 style="color: white; margin: 0;">¡Bienvenido a Bakery Forecast!</h1>
    </div>
    
    <div style="padding: 20px;">
        <p>Hola <strong>{{user_name}}</strong>,</p>
        
        <p>Gracias por registrarte en nuestra plataforma de pronóstico para panaderías.</p>
        
        <p>Tu cuenta ha sido creada exitosamente y ya puedes comenzar a:</p>
        
        <ul style="color: #333;">
            <li>📊 Subir datos de ventas</li>
            <li>🔮 Generar pronósticos de demanda</li>
            <li>⚡ Optimizar tu producción</li>
        </ul>
        
        <div style="text-align: center; margin: 30px 0;">
            <a href="{{dashboard_url}}" 
               style="background: #667eea; color: white; padding: 12px 30px; 
                      text-decoration: none; border-radius: 5px; font-weight: bold;">
                Ir al Dashboard
            </a>
        </div>
        
        <p>Si tienes alguna pregunta, no dudes en contactarnos.</p>
        
        <p>Saludos,<br>
        <strong>El equipo de Bakery Forecast</strong></p>
    </div>
    
    <div style="background: #f8f9fa; padding: 15px; text-align: center; font-size: 12px; color: #666;">
        © 2025 Bakery Forecast. Todos los derechos reservados.
    </div>
</body>
</html>
                """
            },
            "forecast_alert": {
                "text": """
Alerta de Pronóstico - {{bakery_name}}

Se ha detectado una variación significativa en la demanda prevista:

Producto: {{product_name}}
Fecha: {{forecast_date}}
Demanda prevista: {{predicted_demand}} unidades
Variación: {{variation_percentage}}%

{{alert_message}}

Revisa los pronósticos en: {{dashboard_url}}

Saludos,
El equipo de Bakery Forecast
                """,
                "html": """
<html>
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
    <div style="background: #ff6b6b; padding: 20px; text-align: center;">
        <h1 style="color: white; margin: 0;">🚨 Alerta de Pronóstico</h1>
    </div>
    
    <div style="padding: 20px;">
        <h2 style="color: #333;">{{bakery_name}}</h2>
        
        <p>Se ha detectado una variación significativa en la demanda prevista:</p>
        
        <div style="background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0;">
            <p><strong>Producto:</strong> {{product_name}}</p>
            <p><strong>Fecha:</strong> {{forecast_date}}</p>
            <p><strong>Demanda prevista:</strong> {{predicted_demand}} unidades</p>
            <p><strong>Variación:</strong> <span style="color: #ff6b6b; font-weight: bold;">{{variation_percentage}}%</span></p>
        </div>
        
        <div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 5px; margin: 20px 0;">
            <p style="margin: 0; color: #856404;">{{alert_message}}</p>
        </div>
        
        <div style="text-align: center; margin: 30px 0;">
            <a href="{{dashboard_url}}" 
               style="background: #ff6b6b; color: white; padding: 12px 30px; 
                      text-decoration: none; border-radius: 5px; font-weight: bold;">
                Ver Pronósticos
            </a>
        </div>
    </div>
</body>
</html>
                """
            },
            "weekly_report": {
                "text": """
Reporte Semanal - {{bakery_name}}

Resumen de la semana del {{week_start}} al {{week_end}}:

Ventas Totales: {{total_sales}} unidades
Precisión del Pronóstico: {{forecast_accuracy}}%
Productos más vendidos:
{{#top_products}}
- {{name}}: {{quantity}} unidades
{{/top_products}}

Recomendaciones:
{{recommendations}}

Ver reporte completo: {{report_url}}

Saludos,
El equipo de Bakery Forecast
                """,
                "html": """
<html>
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
    <div style="background: #74b9ff; padding: 20px; text-align: center;">
        <h1 style="color: white; margin: 0;">📊 Reporte Semanal</h1>
    </div>
    
    <div style="padding: 20px;">
        <h2 style="color: #333;">{{bakery_name}}</h2>
        
        <p style="color: #666;">Semana del {{week_start}} al {{week_end}}</p>
        
        <div style="display: flex; gap: 20px; margin: 20px 0;">
            <div style="background: #dff0d8; padding: 15px; border-radius: 5px; flex: 1; text-align: center;">
                <h3 style="margin: 0; color: #3c763d;">{{total_sales}}</h3>
                <p style="margin: 5px 0; color: #3c763d;">Ventas Totales</p>
            </div>
            <div style="background: #d1ecf1; padding: 15px; border-radius: 5px; flex: 1; text-align: center;">
                <h3 style="margin: 0; color: #0c5460;">{{forecast_accuracy}}%</h3>
                <p style="margin: 5px 0; color: #0c5460;">Precisión</p>
            </div>
        </div>
        
        <h3 style="color: #333;">Productos más vendidos:</h3>
        <ul style="color: #333;">
            {{#top_products}}
            <li><strong>{{name}}</strong>: {{quantity}} unidades</li>
            {{/top_products}}
        </ul>
        
        <div style="background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 20px 0;">
            <h4 style="margin-top: 0; color: #004085;">Recomendaciones:</h4>
            <p style="margin-bottom: 0; color: #004085;">{{recommendations}}</p>
        </div>
        
        <div style="text-align: center; margin: 30px 0;">
            <a href="{{report_url}}" 
               style="background: #74b9ff; color: white; padding: 12px 30px; 
                      text-decoration: none; border-radius: 5px; font-weight: bold;">
                Ver Reporte Completo
            </a>
        </div>
    </div>
</body>
</html>
                """
            }
        }
        
        return templates.get(template_name)