import base64
from typing import Any, List, Optional

from abs_utils.logger import setup_logger
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import (
    Attachment,
    Bcc,
    Cc,
    Disposition,
    FileContent,
    FileName,
    FileType,
    Mail,
    To,
)

from abs_email_core.provider.base import EmailProvider
from abs_email_core.schema.email_schema import SendMailRequest

logger = setup_logger(__name__)


class SendGridProvider(EmailProvider):
    """SendGrid email provider implementation."""

    def __init__(self, api_key: str, from_email: str):
        self.api_key = api_key
        self.from_email = from_email
        if not self.api_key:
            raise RuntimeError("SendGrid API key is required.")

        self.sg = SendGridAPIClient(self.api_key)

    def send_email(
        self, request: SendMailRequest, attachments: Optional[List[Any]] = None
    ) -> bool:
        message = Mail(
            from_email=request.from_email or self.from_email,
            to_emails=[To(email) for email in request.to],
            subject=request.subject,
            plain_text_content=request.body,
        )

        if request.from_email:
            message.reply_to = request.from_email

        if request.cc:
            message.cc = [Cc(email) for email in request.cc]

        if request.bcc:
            message.bcc = [Bcc(email) for email in request.bcc]

        if attachments:
            for upload_file in attachments:
                if (
                    upload_file
                    and hasattr(upload_file, "filename")
                    and upload_file.filename
                ):
                    file_content = upload_file.file.read()
                    encoded_file = base64.b64encode(file_content).decode()
                    attachment = Attachment()
                    attachment.file_content = FileContent(encoded_file)
                    attachment.file_name = FileName(upload_file.filename)
                    attachment.file_type = FileType("application/octet-stream")
                    attachment.disposition = Disposition("attachment")
                    message.add_attachment(attachment)

        try:
            self.sg.send(message)
            return True

        except Exception as e:
            logger.error(f"SendGrid API error while sending plain text email to {request.to}: {str(e)}", exc_info=True)
            return False

    def send_html_email(
        self, request: SendMailRequest, attachments: Optional[List[Any]] = None
    ) -> bool:
        """Send an HTML email via SendGrid."""
        message = Mail(
            from_email=request.from_email or self.from_email,
            to_emails=[To(email) for email in request.to],
            subject=request.subject,
            html_content=request.body,
        )

        if request.from_email:
            message.reply_to = request.from_email

        if request.cc:
            message.cc = [Cc(email) for email in request.cc]

        if request.bcc:
            message.bcc = [Bcc(email) for email in request.bcc]

        if attachments:
            for upload_file in attachments:
                if (
                    upload_file
                    and hasattr(upload_file, "filename")
                    and upload_file.filename
                ):
                    file_content = upload_file.file.read()
                    encoded_file = base64.b64encode(file_content).decode()
                    attachment = Attachment()
                    attachment.file_content = FileContent(encoded_file)
                    attachment.file_name = FileName(upload_file.filename)
                    attachment.file_type = FileType("application/octet-stream")
                    attachment.disposition = Disposition("attachment")
                    message.add_attachment(attachment)

        try:
            self.sg.send(message)
            return True

        except Exception as e:
            logger.error(f"SendGrid API error while sending HTML email to {request.to}: {str(e)}", exc_info=True)
            return False
