"""Send Email node for sending emails via SMTP."""

import base64
from email.message import EmailMessage
from email.utils import make_msgid
from typing import Any

import aiosmtplib
from flowire_sdk import (
    BaseNode,
    BaseNodeOutput,
    NodeExecutionContext,
    NodeMetadata,
)
from pydantic import BaseModel, Field

from fw_nodes_email.credentials import SMTPCredentialSchema

MAX_ATTACHMENT_SIZE = 25 * 1024 * 1024  # 25 MB


class AttachmentInput(BaseModel):
    """An email attachment."""

    filename: str = Field(..., description="Attachment filename (e.g., report.pdf)")
    content_base64: str = Field(..., description="File content as base64-encoded string")
    content_type: str = Field(
        default="application/octet-stream",
        description="MIME type (e.g., application/pdf, image/png)",
    )


class SendEmailInput(BaseModel):
    """Input for sending an email via SMTP."""

    credential_id: str = Field(
        ...,
        description="SMTP credential with server details and authentication",
    )
    from_address: str = Field(
        ...,
        description="Sender email address (e.g., noreply@example.com)",
    )
    to: str = Field(
        ...,
        description="Recipient email addresses (comma-separated for multiple)",
    )
    subject: str = Field(
        ...,
        description="Email subject line",
    )
    body: str = Field(
        ...,
        description="Email body content",
    )
    is_html: bool = Field(
        default=False,
        description="Send body as HTML instead of plain text",
    )
    cc: str | None = Field(
        default=None,
        description="CC recipients (comma-separated)",
    )
    bcc: str | None = Field(
        default=None,
        description="BCC recipients (comma-separated)",
    )
    reply_to: str | None = Field(
        default=None,
        description="Reply-To address",
    )
    attachments: list[AttachmentInput] | None = Field(
        default=None,
        description="File attachments (base64-encoded content with filename and MIME type)",
    )


class SendEmailOutput(BaseNodeOutput):
    """Output after sending an email."""

    success: bool = Field(..., description="Whether the email was sent successfully")
    message_id: str = Field(..., description="Message-ID header of the sent email")
    recipients: list[str] = Field(..., description="List of recipient addresses the email was sent to")


class SendEmailNode(BaseNode):
    """Send an email via SMTP.

    Supports plain text and HTML emails with CC, BCC, Reply-To headers, and file attachments.
    Requires an SMTP credential with server details and authentication.
    """

    input_schema = SendEmailInput
    output_schema = SendEmailOutput
    credential_schema = SMTPCredentialSchema

    metadata = NodeMetadata(
        name="Send Email",
        description="Send an email via SMTP",
        category="email",
        icon="📧",
        color="#4A90D9",
    )

    async def execute_logic(
        self,
        validated_inputs: dict[str, Any],
        context: NodeExecutionContext,
    ) -> SendEmailOutput:
        """Send email using SMTP."""
        credential_data = await context.resolve_credential(
            credential_id=validated_inputs["credential_id"],
            credential_type=self.get_credential_type(),
        )

        context.register_secret(credential_data["password"])

        # Build email message
        msg = EmailMessage()
        msg["From"] = validated_inputs["from_address"]
        msg["To"] = validated_inputs["to"]
        msg["Subject"] = validated_inputs["subject"]

        if validated_inputs.get("cc"):
            msg["Cc"] = validated_inputs["cc"]

        if validated_inputs.get("reply_to"):
            msg["Reply-To"] = validated_inputs["reply_to"]

        msg["Message-ID"] = make_msgid()

        if validated_inputs.get("is_html"):
            msg.set_content(validated_inputs["body"], subtype="html")
        else:
            msg.set_content(validated_inputs["body"])

        # Add attachments
        attachments = validated_inputs.get("attachments")
        if attachments:
            for att in attachments:
                # Normalize dict (from expression resolution) to AttachmentInput
                if isinstance(att, dict):
                    att = AttachmentInput(**att)

                file_data = base64.b64decode(att.content_base64)
                if len(file_data) > MAX_ATTACHMENT_SIZE:
                    raise ValueError(
                        f"Attachment '{att.filename}' ({len(file_data)} bytes) "
                        f"exceeds maximum size of {MAX_ATTACHMENT_SIZE} bytes"
                    )
                maintype, _, subtype = att.content_type.partition("/")
                msg.add_attachment(
                    file_data,
                    maintype=maintype or "application",
                    subtype=subtype or "octet-stream",
                    filename=att.filename,
                )

        # Collect all recipients for sending
        all_recipients = [addr.strip() for addr in validated_inputs["to"].split(",")]
        if validated_inputs.get("cc"):
            all_recipients.extend(addr.strip() for addr in validated_inputs["cc"].split(","))
        if validated_inputs.get("bcc"):
            all_recipients.extend(addr.strip() for addr in validated_inputs["bcc"].split(","))

        # Send via SMTP
        host = credential_data["host"]
        port = credential_data.get("port", 587)
        username = credential_data["username"]
        password = credential_data["password"]
        use_tls = credential_data.get("use_tls", True)
        use_ssl = credential_data.get("use_ssl", False)

        smtp = aiosmtplib.SMTP(
            hostname=host,
            port=port,
            use_tls=use_ssl,  # use_tls in aiosmtplib means implicit SSL
            start_tls=use_tls and not use_ssl,  # STARTTLS auto-negotiated during connect
            timeout=30,
        )
        try:
            await smtp.connect()
            await smtp.login(username, password)
            await smtp.send_message(msg, recipients=all_recipients)
        except aiosmtplib.SMTPException as e:
            raise RuntimeError(f"SMTP error: {e}") from e
        finally:
            try:
                await smtp.quit()
            except Exception:
                pass

        message_id = msg["Message-ID"]

        return SendEmailOutput(
            success=True,
            message_id=message_id,
            recipients=all_recipients,
        )
