import smtplib
import ssl
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import datetime
from typing import Optional


class EmailSender:
    """
    A flexible email notification library for sending HTML emails via SMTP.
    
    Supports two modes:
    1. Wrap mode: Automatically wrap messages with HTML styling
    2. Direct mode: Send pre-formatted HTML content directly
    
    Attributes:
        smtp_server (str): SMTP server address (default: smtp.gmail.com)
        smtp_port (int): SMTP port (default: 587)
        sender_email (str): Email address to send from
        sender_password (str): Email password or app password
        recipient_email (str): Default recipient email address
    """
    
    def __init__(
        self,
        sender_email: str,
        sender_password: str,
        recipient_email: str,
        smtp_server: str = "smtp.gmail.com",
        smtp_port: int = 587
    ):
        """
        Initialize the EmailSender with SMTP configuration.
        
        Args:
            sender_email (str): Email address to send from
            sender_password (str): Email password or app password
            recipient_email (str): Default recipient email address
            smtp_server (str, optional): SMTP server address. Defaults to "smtp.gmail.com"
            smtp_port (int, optional): SMTP port. Defaults to 587
            
        Raises:
            ValueError: If any required parameter is empty
        """
        if not sender_email:
            raise ValueError("sender_email cannot be empty")
        if not sender_password:
            raise ValueError("sender_password cannot be empty")
        if not recipient_email:
            raise ValueError("recipient_email cannot be empty")
        if not smtp_server:
            raise ValueError("smtp_server cannot be empty")
        
        self.smtp_server = smtp_server
        self.smtp_port = smtp_port
        self.sender_email = sender_email
        self.sender_password = sender_password
        self.recipient_email = recipient_email
    
    def _create_html_content(self, subject: str, message: str, details: Optional[dict] = None) -> str:
        """
        Creates HTML email content for notifications with optional details.
        
        Args:
            subject: Email subject/title
            message: Main notification message
            details: Optional dictionary with additional details
            
        Returns:
            HTML formatted email content
        """
        details_html = ""
        if details:
            details_html = "<h3>Details</h3><ul>"
            for key, value in details.items():
                details_html += f"<li><strong>{key}:</strong> {value}</li>"
            details_html += "</ul>"
        
        html = f"""
        <html>
        <head>
            <style>
                body {{ font-family: Arial, sans-serif; margin: 20px; color: #333; }}
                .header {{ background-color: #0066cc; color: white; padding: 15px; border-radius: 5px; }}
                .content {{ margin: 20px 0; padding: 15px; background-color: #f8f9fa; border-left: 4px solid #0066cc; }}
                .details {{ margin: 20px 0; }}
                .timestamp {{ color: #666; font-size: 0.9em; margin-top: 20px; }}
                ul {{ line-height: 1.8; }}
            </style>
        </head>
        <body>
            <div class="header">
                <h2>📧 {subject}</h2>
            </div>
            
            <div class="content">
                <p>{message}</p>
            </div>
            
            <div class="details">
                {details_html}
            </div>
            
            <div class="timestamp">
                <p><em>Notification sent: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</em></p>
            </div>
        </body>
        </html>
        """
        return html
    
    def send_notification(
        self, 
        subject: str, 
        message: Optional[str] = None,
        html_content: Optional[str] = None,
        details: Optional[dict] = None,
        recipient: Optional[str] = None
    ) -> bool:
        """
        Sends a notification email.
        
        Supports two modes:
        1. Wrap mode: Provide subject, message, and optional details to auto-wrap with HTML
        2. Direct mode: Provide subject and html_content directly
        
        Args:
            subject: Email subject line
            message: Main notification message (used in wrap mode)
            html_content: Pre-formatted HTML content (used in direct mode). Takes precedence over message/details
            details: Optional dictionary with notification details (used in wrap mode)
            recipient: Optional recipient email (uses default if not provided)
            
        Returns:
            True if email sent successfully, False otherwise
        """
        try:
            # Determine which HTML content to use
            if html_content:
                # Direct mode: use provided HTML
                final_html = html_content
            elif message:
                # Wrap mode: create HTML from message and details
                final_html = self._create_html_content(subject, message, details)
            else:
                print("❌ Either 'message' or 'html_content' must be provided")
                return False
            
            # Create message
            msg = MIMEMultipart('alternative')
            msg['Subject'] = subject
            msg['From'] = self.sender_email
            msg['To'] = recipient if recipient else self.recipient_email
            
            # Attach HTML content
            msg.attach(MIMEText(final_html, 'html'))
            
            # Send email
            with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
                server.starttls(context=ssl.create_default_context())
                server.login(self.sender_email, self.sender_password)
                server.send_message(msg)
            
            print("[OK] Notification sent successfully")
            return True
            
        except smtplib.SMTPAuthenticationError:
            print("[ERROR] Gmail authentication failed. Check credentials.")
            return False
        except Exception as e:
            print(f"[ERROR] Failed to send email: {str(e)}")
            return False
    
    def send_failure_notification(
        self, 
        subject: str, 
        message: str, 
        details: Optional[dict] = None,
        recipient: Optional[str] = None
    ) -> bool:
        """
        Sends a failure notification email (legacy method).
        Wraps message and details with failure-styled HTML content.
        
        Args:
            subject: Email subject line
            message: Main failure message
            details: Optional dictionary with failure details
            recipient: Optional recipient email (uses default if not provided)
            
        Returns:
            True if email sent successfully, False otherwise
        """
        return self.send_notification(
            subject=subject,
            message=message,
            details=details,
            recipient=recipient
        )
