from email.message import EmailMessage as StdEmailMessage
from email.utils import formataddr
import logging

import aiosmtplib

from mode.email.base import EmailMessage, EmailProviderError


log = logging.getLogger("qycw.email.genericsmtp")


class GenericSmtpEmailProvider:
    name = "genericsmtp"

    def __init__(self, settings):
        self.host = _text(settings, "SMTP_HOST")
        self.port = _int(settings, "SMTP_PORT", 465)
        self.user = _text(settings, "SMTP_USER")
        self.password = _text(settings, "SMTP_PASSWORD")
        self.from_email = _text(settings, "SMTP_FROM_EMAIL")
        self.from_name = _text(settings, "SMTP_FROM_NAME", "QingYun Cloud")
        self.security = _text(settings, "SMTP_SECURITY", "ssl").lower()
        self.auth_enabled = _bool(settings, "SMTP_AUTH_ENABLED", True)
        self.validate_certs = _bool(settings, "SMTP_VALIDATE_CERTS", True)
        self.timeout = _int(settings, "SMTP_TIMEOUT", 15)
        self.reply_to = _text(settings, "SMTP_REPLY_TO")

    async def send(self, message: EmailMessage) -> None:
        self._ensure_configured()

        msg = StdEmailMessage()
        msg["From"] = formataddr((self.from_name, self.from_email))
        msg["To"] = message.to
        msg["Subject"] = message.subject
        if self.reply_to:
            msg["Reply-To"] = self.reply_to
        msg.set_content(message.body, subtype=message.subtype, charset="utf-8")

        kwargs = self._smtp_kwargs()
        log.debug(
            "sending email via generic smtp to=%s subject=%s smtp=%s:%s security=%s auth=%s",
            message.to,
            message.subject,
            self.host,
            self.port,
            self.security,
            self.auth_enabled,
        )
        try:
            await aiosmtplib.send(msg, **kwargs)
        except Exception as exc:  # noqa: BLE001
            log.error("generic smtp send to %s failed: %s", message.to, exc)
            raise EmailProviderError("generic smtp email send failed") from exc

        log.info("generic smtp email sent to=%s subject=%s", message.to, message.subject)

    def _smtp_kwargs(self) -> dict:
        kwargs = {
            "hostname": self.host,
            "port": self.port,
            "timeout": self.timeout,
            "validate_certs": self.validate_certs,
        }
        if self.auth_enabled:
            kwargs["username"] = self.user
            kwargs["password"] = self.password
        if self.security == "ssl":
            kwargs["use_tls"] = True
        elif self.security == "starttls":
            kwargs["use_tls"] = False
            kwargs["start_tls"] = True
        elif self.security == "none":
            kwargs["use_tls"] = False
            kwargs["start_tls"] = False
        else:
            raise EmailProviderError("SMTP_SECURITY must be one of: ssl, starttls, none", status_code=400)
        return kwargs

    def _ensure_configured(self) -> None:
        missing = [
            name
            for name, value in {
                "SMTP_HOST": self.host,
                "SMTP_FROM_EMAIL": self.from_email,
            }.items()
            if not value
        ]
        if self.auth_enabled:
            if not self.user:
                missing.append("SMTP_USER")
            if not self.password:
                missing.append("SMTP_PASSWORD")
        if missing:
            raise EmailProviderError(f"generic smtp provider missing config: {', '.join(missing)}")


def _text(settings, name: str, default: str = "") -> str:
    return str(getattr(settings, name, default) or "").strip()


def _int(settings, name: str, default: int) -> int:
    try:
        return int(getattr(settings, name, default))
    except (TypeError, ValueError):
        return default


def _bool(settings, name: str, default: bool) -> bool:
    value = getattr(settings, name, default)
    if isinstance(value, bool):
        return value
    if isinstance(value, str):
        return value.strip().lower() in {"1", "true", "yes", "on", "enabled"}
    return bool(value)


def create_provider(settings) -> GenericSmtpEmailProvider:
    return GenericSmtpEmailProvider(settings)

