"""SMTP email provider using Python's bounded standard library client."""

from __future__ import annotations

import smtplib
import ssl
import time
from email.message import EmailMessage

from .base import ProviderConfigurationError, ProviderResult


class SMTPDriver:
    def validate_configuration(self, config: dict) -> None:
        if not str(config.get("host") or "").strip():
            raise ProviderConfigurationError("SMTP host is required")
        if str(config.get("encryption") or "starttls").lower() not in {"none", "starttls", "ssl"}:
            raise ProviderConfigurationError("Unsupported SMTP encryption mode")
        port = int(config.get("port") or 0)
        if not 1 <= port <= 65535:
            raise ProviderConfigurationError("SMTP port is invalid")
        if not str(config.get("from_email") or "").strip():
            raise ProviderConfigurationError("SMTP from_email is required")

    def _connect(self, config: dict):
        self.validate_configuration(config)
        host, port = str(config["host"]), int(config["port"])
        timeout = float(config.get("timeout", 10))
        if str(config.get("encryption", "starttls")).lower() == "ssl":
            return smtplib.SMTP_SSL(host, port, timeout=timeout, context=ssl.create_default_context())
        client = smtplib.SMTP(host, port, timeout=timeout)
        if str(config.get("encryption", "starttls")).lower() == "starttls":
            client.starttls(context=ssl.create_default_context())
        return client

    def _login(self, client, config: dict, secret: str | None) -> None:
        username = str(config.get("username") or "")
        if username:
            if not secret:
                raise smtplib.SMTPAuthenticationError(535, b"credential missing")
            client.login(username, secret)

    def health_check(self, config: dict, secret: str | None) -> ProviderResult:
        started = time.monotonic()
        try:
            with self._connect(config) as client:
                self._login(client, config, secret)
                code, _ = client.noop()
            ok = 200 <= int(code) < 400
            return ProviderResult(ok, "active" if ok else "degraded", str(code),
                                  None if ok else "provider_unavailable", not ok,
                                  int((time.monotonic() - started) * 1000))
        except ProviderConfigurationError:
            return ProviderResult(False, "credential_error", error_category="invalid_configuration")
        except smtplib.SMTPAuthenticationError:
            return ProviderResult(False, "credential_error", error_category="credential_error")
        except (OSError, smtplib.SMTPException):
            return ProviderResult(False, "offline", error_category="network_error", retryable=True,
                                  latency_ms=int((time.monotonic() - started) * 1000))

    def send(self, config: dict, secret: str | None, message: dict) -> ProviderResult:
        started = time.monotonic()
        email = EmailMessage()
        email["From"] = f'{config.get("from_name")} <{config["from_email"]}>' if config.get("from_name") else config["from_email"]
        email["To"] = message["recipient"]
        email["Subject"] = message.get("subject") or ""
        if config.get("reply_to"):
            email["Reply-To"] = config["reply_to"]
        email.set_content(message["text"])
        if message.get("html"):
            email.add_alternative(message["html"], subtype="html")
        try:
            with self._connect(config) as client:
                self._login(client, config, secret)
                refused = client.send_message(email)
            if refused:
                return ProviderResult(False, "failed", error_category="invalid_recipient")
            return ProviderResult(True, "accepted", "250", latency_ms=int((time.monotonic() - started) * 1000))
        except ProviderConfigurationError:
            return ProviderResult(False, "failed", error_category="invalid_configuration")
        except smtplib.SMTPAuthenticationError:
            return ProviderResult(False, "failed", error_category="credential_error")
        except smtplib.SMTPRecipientsRefused:
            return ProviderResult(False, "failed", error_category="invalid_recipient")
        except smtplib.SMTPResponseException as exc:
            retryable = 400 <= exc.smtp_code < 500
            return ProviderResult(False, "failed", str(exc.smtp_code), "provider_unavailable", retryable)
        except (OSError, smtplib.SMTPException):
            return ProviderResult(False, "failed", error_category="network_error", retryable=True,
                                  latency_ms=int((time.monotonic() - started) * 1000))
