import logging
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

from app.core.config import (
    SMTP_FROM,
    SMTP_HOST,
    SMTP_PASSWORD,
    SMTP_PORT,
    SMTP_SSL,
    SMTP_TLS,
    SMTP_USER,
)

logger = logging.getLogger(__name__)

PURPOSE_SUBJECTS = {
    "signup": "Verify your Sergio account",
    "forgot_password": "Reset your Sergio password",
}


class EmailDeliveryError(Exception):
    """Raised when OTP email cannot be sent."""


def _validate_smtp_config():
    missing = [
        name
        for name, value in {
            "SMTP_HOST": SMTP_HOST,
            "SMTP_PORT": SMTP_PORT,
            "SMTP_PASSWORD": SMTP_PASSWORD,
            "SMTP_FROM": SMTP_FROM,
            "SMTP_USER": SMTP_USER,
        }.items()
        if not value
    ]
    if missing:
        raise EmailDeliveryError(
            f"SMTP is not configured. Missing: {', '.join(missing)}"
        )


def _build_message(to_email: str, otp: str, purpose: str) -> MIMEMultipart:
    subject = PURPOSE_SUBJECTS.get(purpose, "Your verification code")
    body = (
        f"Your verification code is: {otp}\n\n"
        "This code expires in 10 minutes.\n"
        "If you did not request this, you can ignore this email."
    )

    message = MIMEMultipart()
    message["From"] = SMTP_FROM
    message["To"] = to_email
    message["Subject"] = subject
    message.attach(MIMEText(body, "plain"))
    return message


def _smtp_connection():
    if SMTP_SSL:
        server = smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, timeout=30)
        server.ehlo()
        return server

    server = smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=30)
    server.ehlo()
    if SMTP_TLS:
        server.starttls()
        server.ehlo()
    return server


def send_email(to_email: str, otp: str, purpose: str = "verification") -> None:
    _validate_smtp_config()

    message = _build_message(to_email, otp, purpose)

    try:
        with _smtp_connection() as server:
            server.login(SMTP_USER, SMTP_PASSWORD)
            server.sendmail(SMTP_FROM, [to_email], message.as_string())
        logger.info("OTP email sent to %s for %s", to_email, purpose)
    except smtplib.SMTPAuthenticationError as exc:
        logger.exception("SMTP authentication failed for %s", SMTP_USER)
        raise EmailDeliveryError(
            "SMTP login failed. Check SMTP_HOST matches your email provider "
            "(Gmail → smtp.gmail.com, Titan → smtp.titan.email) and use an app password."
        ) from exc
    except smtplib.SMTPException as exc:
        logger.exception("Failed to send OTP email to %s", to_email)
        raise EmailDeliveryError(f"Failed to send OTP email: {exc}") from exc
