import smtplib
from email.message import EmailMessage
from pathlib import Path
from typing import Optional

from ..exceptions import MailError
from ..models.mail_request import MailRequest
from ..utils.logger import get_mail_logger
from ..utils.template_engine import TemplateEngine
from ..utils.validator import validate_mail_request
from .base import BaseMailer

logger = get_mail_logger()


class SMTPMailer(BaseMailer):
    """
    Implementación SMTP para envío de correos.
    """

    def __init__(
        self,
        host: str,
        port: int,
        user: Optional[str] = None,
        password: Optional[str] = None,
        use_tls: bool = True,
        template_engine: Optional[TemplateEngine] = None,
    ):
        if not host:
            raise MailError("host es obligatorio.")
        if not isinstance(port, int) or port <= 0:
            raise MailError("port debe ser un entero positivo.")

        self.host = host
        self.port = port
        self.user = user
        self.password = password
        self.use_tls = use_tls
        self.template_engine = template_engine

    def send_email(self, req: MailRequest) -> None:
        validate_mail_request(req)

        html_body = None
        if req.template:
            if self.template_engine is None:
                raise MailError(
                    "MailRequest incluye template pero SMTPMailer no tiene template_engine configurado."
                )
            logger.debug("Renderizando template SMTP.")
            html_body = self.template_engine.render(req.template, req.context)

        email = EmailMessage()
        email["From"] = req.sender
        email["To"] = ", ".join(req.to)
        email["Subject"] = req.subject

        if req.cc:
            email["Cc"] = ", ".join(req.cc)

        recipients = list(req.to) + list(req.cc) + list(req.bcc)

        if html_body:
            email.set_content(req.message or " ")
            email.add_alternative(html_body, subtype="html")
        else:
            email.set_content(req.message)

        for file_path in req.attachments:
            file = Path(file_path)
            try:
                email.add_attachment(
                    file.read_bytes(),
                    maintype="application",
                    subtype="octet-stream",
                    filename=file.name,
                )
            except Exception as e:
                raise MailError(f"Error adjuntando archivo {file_path}: {e}") from e

        try:
            logger.info("Conectando a servidor SMTP.")
            with smtplib.SMTP(self.host, self.port) as smtp:
                smtp.ehlo()

                if self.use_tls:
                    smtp.starttls()
                    smtp.ehlo()

                if self.user and self.password:
                    smtp.login(self.user, self.password)

                smtp.send_message(email, to_addrs=recipients)

            logger.info("Mail enviado correctamente por SMTP.")
        except Exception as e:
            logger.exception("Error enviando mail por SMTP.")
            raise MailError(f"Error enviando mail por SMTP: {e}") from e