import base64
from pathlib import Path
from typing import Optional

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import (
    Attachment,
    Bcc,
    Cc,
    Content,
    Disposition,
    Email,
    FileContent,
    FileName,
    FileType,
    Mail,
    To,
)

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 SendGridMailer(BaseMailer):
    """
    Implementación de envío usando SendGrid.
    """

    def __init__(
        self,
        api_key: str,
        sender: str,
        template_engine: Optional[TemplateEngine] = None,
    ):
        if not api_key:
            raise MailError("api_key es obligatorio.")
        if not sender:
            raise MailError("sender es obligatorio.")

        self.api_key = api_key
        self.sender = sender
        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 SendGridMailer no tiene template_engine configurado."
                )
            logger.debug("Renderizando template SendGrid.")
            html_body = self.template_engine.render(req.template, req.context)

        message = Mail(
            from_email=Email(req.sender),
            to_emails=[To(addr) for addr in req.to],
            subject=req.subject,
        )

        if req.cc:
            message.cc = [Cc(addr) for addr in req.cc]

        if req.bcc:
            message.bcc = [Bcc(addr) for addr in req.bcc]

        if html_body:
            message.add_content(Content("text/plain", req.message or " "))
            message.add_content(Content("text/html", html_body))
        else:
            message.add_content(Content("text/plain", req.message))

        attachments = []
        for file_path in req.attachments:
            file = Path(file_path)
            try:
                encoded = base64.b64encode(file.read_bytes()).decode()
                attachments.append(
                    Attachment(
                        FileContent(encoded),
                        FileName(file.name),
                        FileType("application/octet-stream"),
                        Disposition("attachment"),
                    )
                )
            except Exception as e:
                raise MailError(f"Error adjuntando archivo {file_path}: {e}") from e

        if attachments:
            message.attachment = attachments

        try:
            logger.info("Enviando mail por SendGrid.")
            sg = SendGridAPIClient(self.api_key)
            response = sg.send(message)
            logger.info(f"Mail enviado correctamente por SendGrid. Status={response.status_code}")
        except Exception as e:
            logger.exception("Error enviando correo por SendGrid.")
            raise MailError(f"Error enviando correo por SendGrid: {e}") from e