"""
邮件发送服务
提供 SMTP 邮件发送功能
"""

import asyncio
import smtplib
import ssl
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from typing import Optional, Tuple

try:
    import aiosmtplib

    AIOSMTPLIB_AVAILABLE = True
except ImportError:
    AIOSMTPLIB_AVAILABLE = False
    aiosmtplib = None


def _create_ssl_context() -> ssl.SSLContext:
    """创建 SSL 上下文，使用 certifi 证书或系统默认证书"""
    try:
        import certifi

        context = ssl.create_default_context(cafile=certifi.where())
    except ImportError:
        context = ssl.create_default_context()
    return context

from sqlalchemy.orm import Session

from src.core.crypto import crypto_service
from src.core.logger import logger
from src.services.system.config import SystemConfigService

from .email_template import EmailTemplate


class EmailSenderService:
    """邮件发送服务"""

    # SMTP 超时配置（秒）
    SMTP_TIMEOUT = 30

    @staticmethod
    def _get_smtp_config(db: Session) -> dict:
        """
        从数据库获取 SMTP 配置

        Args:
            db: 数据库会话

        Returns:
            SMTP 配置字典
        """
        # 获取加密的密码并解密
        encrypted_password = SystemConfigService.get_config(db, "smtp_password")
        smtp_password = None
        if encrypted_password:
            try:
                smtp_password = crypto_service.decrypt(encrypted_password, silent=True)
            except Exception:
                # 解密失败，可能是旧的未加密密码，直接使用
                smtp_password = encrypted_password

        config = {
            "smtp_host": SystemConfigService.get_config(db, "smtp_host"),
            "smtp_port": SystemConfigService.get_config(db, "smtp_port", default=587),
            "smtp_user": SystemConfigService.get_config(db, "smtp_user"),
            "smtp_password": smtp_password,
            "smtp_use_tls": SystemConfigService.get_config(db, "smtp_use_tls", default=True),
            "smtp_use_ssl": SystemConfigService.get_config(db, "smtp_use_ssl", default=False),
            "smtp_from_email": SystemConfigService.get_config(db, "smtp_from_email"),
            "smtp_from_name": SystemConfigService.get_config(db, "smtp_from_name", default="Aether"),
        }
        return config

    @staticmethod
    def _validate_smtp_config(config: dict) -> Tuple[bool, Optional[str]]:
        """
        验证 SMTP 配置

        Args:
            config: SMTP 配置字典

        Returns:
            (是否有效, 错误信息)
        """
        required_fields = ["smtp_host", "smtp_from_email"]

        for field in required_fields:
            if not config.get(field):
                return False, f"缺少必要的 SMTP 配置: {field}"

        return True, None

    @staticmethod
    async def send_verification_code(
        db: Session, to_email: str, code: str, expire_minutes: int = 30
    ) -> Tuple[bool, Optional[str]]:
        """
        发送验证码邮件

        Args:
            db: 数据库会话
            to_email: 收件人邮箱
            code: 验证码
            expire_minutes: 过期时间（分钟）

        Returns:
            (是否发送成功, 错误信息)
        """
        # 获取 SMTP 配置
        config = EmailSenderService._get_smtp_config(db)

        # 验证配置
        valid, error = EmailSenderService._validate_smtp_config(config)
        if not valid:
            logger.error(f"SMTP 配置无效: {error}")
            return False, error

        # 生成邮件内容
        # 优先使用 email_app_name，否则回退到 smtp_from_name
        app_name = SystemConfigService.get_config(db, "email_app_name", default=None)
        if not app_name:
            app_name = SystemConfigService.get_config(db, "smtp_from_name", default="Aether")

        html_body = EmailTemplate.get_verification_code_html(
            code=code, expire_minutes=expire_minutes, db=db, app_name=app_name, email=to_email
        )
        text_body = EmailTemplate.get_verification_code_text(
            code=code, expire_minutes=expire_minutes, db=db, app_name=app_name, email=to_email
        )
        subject = EmailTemplate.get_subject("verification", db=db)

        # 发送邮件
        return await EmailSenderService._send_email(
            config=config, to_email=to_email, subject=subject, html_body=html_body, text_body=text_body
        )

    @staticmethod
    async def _send_email(
        config: dict,
        to_email: str,
        subject: str,
        html_body: Optional[str] = None,
        text_body: Optional[str] = None,
    ) -> Tuple[bool, Optional[str]]:
        """
        发送邮件（内部方法）

        Args:
            config: SMTP 配置
            to_email: 收件人邮箱
            subject: 邮件主题
            html_body: HTML 邮件内容
            text_body: 纯文本邮件内容

        Returns:
            (是否发送成功, 错误信息)
        """
        if AIOSMTPLIB_AVAILABLE:
            return await EmailSenderService._send_email_async(
                config, to_email, subject, html_body, text_body
            )
        else:
            return await EmailSenderService._send_email_sync_wrapper(
                config, to_email, subject, html_body, text_body
            )

    @staticmethod
    async def _send_email_async(
        config: dict,
        to_email: str,
        subject: str,
        html_body: Optional[str] = None,
        text_body: Optional[str] = None,
    ) -> Tuple[bool, Optional[str]]:
        """
        异步发送邮件（使用 aiosmtplib）

        Args:
            config: SMTP 配置
            to_email: 收件人邮箱
            subject: 邮件主题
            html_body: HTML 邮件内容
            text_body: 纯文本邮件内容

        Returns:
            (是否发送成功, 错误信息)
        """
        try:
            # 构建邮件
            message = MIMEMultipart("alternative")
            message["Subject"] = subject
            message["From"] = f"{config['smtp_from_name']} <{config['smtp_from_email']}>"
            message["To"] = to_email

            # 添加纯文本部分
            if text_body:
                message.attach(MIMEText(text_body, "plain", "utf-8"))

            # 添加 HTML 部分
            if html_body:
                message.attach(MIMEText(html_body, "html", "utf-8"))

            # 发送邮件
            ssl_context = _create_ssl_context()
            if config["smtp_use_ssl"]:
                await aiosmtplib.send(
                    message,
                    hostname=config["smtp_host"],
                    port=config["smtp_port"],
                    use_tls=True,
                    tls_context=ssl_context,
                    username=config["smtp_user"],
                    password=config["smtp_password"],
                    timeout=EmailSenderService.SMTP_TIMEOUT,
                )
            else:
                await aiosmtplib.send(
                    message,
                    hostname=config["smtp_host"],
                    port=config["smtp_port"],
                    start_tls=config["smtp_use_tls"],
                    tls_context=ssl_context if config["smtp_use_tls"] else None,
                    username=config["smtp_user"],
                    password=config["smtp_password"],
                    timeout=EmailSenderService.SMTP_TIMEOUT,
                )

            logger.info(f"验证码邮件发送成功: {to_email}")
            return True, None

        except Exception as e:
            error_msg = f"发送邮件失败: {str(e)}"
            logger.error(error_msg)
            return False, error_msg

    @staticmethod
    async def _send_email_sync_wrapper(
        config: dict,
        to_email: str,
        subject: str,
        html_body: Optional[str] = None,
        text_body: Optional[str] = None,
    ) -> Tuple[bool, Optional[str]]:
        """
        同步邮件发送的异步包装器

        Args:
            config: SMTP 配置
            to_email: 收件人邮箱
            subject: 邮件主题
            html_body: HTML 邮件内容
            text_body: 纯文本邮件内容

        Returns:
            (是否发送成功, 错误信息)
        """
        loop = asyncio.get_event_loop()
        return await loop.run_in_executor(
            None, EmailSenderService._send_email_sync, config, to_email, subject, html_body, text_body
        )

    @staticmethod
    def _send_email_sync(
        config: dict,
        to_email: str,
        subject: str,
        html_body: Optional[str] = None,
        text_body: Optional[str] = None,
    ) -> Tuple[bool, Optional[str]]:
        """
        同步发送邮件（使用标准库 smtplib）

        Args:
            config: SMTP 配置
            to_email: 收件人邮箱
            subject: 邮件主题
            html_body: HTML 邮件内容
            text_body: 纯文本邮件内容

        Returns:
            (是否发送成功, 错误信息)
        """
        try:
            # 构建邮件
            message = MIMEMultipart("alternative")
            message["Subject"] = subject
            message["From"] = f"{config['smtp_from_name']} <{config['smtp_from_email']}>"
            message["To"] = to_email

            # 添加纯文本部分
            if text_body:
                message.attach(MIMEText(text_body, "plain", "utf-8"))

            # 添加 HTML 部分
            if html_body:
                message.attach(MIMEText(html_body, "html", "utf-8"))

            # 连接 SMTP 服务器
            server = None
            ssl_context = _create_ssl_context()
            try:
                if config["smtp_use_ssl"]:
                    server = smtplib.SMTP_SSL(
                        config["smtp_host"],
                        config["smtp_port"],
                        context=ssl_context,
                        timeout=EmailSenderService.SMTP_TIMEOUT,
                    )
                else:
                    server = smtplib.SMTP(
                        config["smtp_host"],
                        config["smtp_port"],
                        timeout=EmailSenderService.SMTP_TIMEOUT,
                    )
                    if config["smtp_use_tls"]:
                        server.starttls(context=ssl_context)

                # 登录
                if config["smtp_user"] and config["smtp_password"]:
                    server.login(config["smtp_user"], config["smtp_password"])

                # 发送邮件
                server.send_message(message)

                logger.info(f"验证码邮件发送成功（同步方式）: {to_email}")
                return True, None
            finally:
                # 确保服务器连接被关闭
                if server is not None:
                    try:
                        server.quit()
                    except Exception as quit_error:
                        logger.warning(f"关闭 SMTP 连接时出错: {quit_error}")

        except Exception as e:
            error_msg = f"发送邮件失败: {str(e)}"
            logger.error(error_msg)
            return False, error_msg

    @staticmethod
    async def test_smtp_connection(
        db: Session, override_config: Optional[dict] = None
    ) -> Tuple[bool, Optional[str]]:
        """
        测试 SMTP 连接

        Args:
            db: 数据库会话
            override_config: 可选的覆盖配置（通常来自未保存的前端表单）

        Returns:
            (是否连接成功, 错误信息)
        """
        config = EmailSenderService._get_smtp_config(db)

        # 用外部传入的配置覆盖（仅覆盖提供的字段）
        if override_config:
            config.update({k: v for k, v in override_config.items() if v is not None})

        # 验证配置
        valid, error = EmailSenderService._validate_smtp_config(config)
        if not valid:
            return False, error

        try:
            ssl_context = _create_ssl_context()
            if AIOSMTPLIB_AVAILABLE:
                # 使用异步方式测试
                # 注意: use_tls=True 表示隐式 SSL (端口 465)
                # start_tls=True 表示 STARTTLS (端口 587)
                use_ssl = config["smtp_use_ssl"]
                use_starttls = config["smtp_use_tls"] and not use_ssl

                smtp = aiosmtplib.SMTP(
                    hostname=config["smtp_host"],
                    port=config["smtp_port"],
                    use_tls=use_ssl,
                    start_tls=use_starttls,
                    tls_context=ssl_context if (use_ssl or use_starttls) else None,
                    timeout=EmailSenderService.SMTP_TIMEOUT,
                )
                await smtp.connect()

                if config["smtp_user"] and config["smtp_password"]:
                    await smtp.login(config["smtp_user"], config["smtp_password"])

                await smtp.quit()
            else:
                # 使用同步方式测试
                if config["smtp_use_ssl"]:
                    server = smtplib.SMTP_SSL(
                        config["smtp_host"],
                        config["smtp_port"],
                        context=ssl_context,
                        timeout=EmailSenderService.SMTP_TIMEOUT,
                    )
                else:
                    server = smtplib.SMTP(
                        config["smtp_host"],
                        config["smtp_port"],
                        timeout=EmailSenderService.SMTP_TIMEOUT,
                    )
                    if config["smtp_use_tls"]:
                        server.starttls(context=ssl_context)

                if config["smtp_user"] and config["smtp_password"]:
                    server.login(config["smtp_user"], config["smtp_password"])

                server.quit()

            logger.info("SMTP 连接测试成功")
            return True, None

        except Exception as e:
            error_msg = _translate_smtp_error(str(e))
            logger.error(f"SMTP 连接测试失败: {error_msg}")
            return False, error_msg


def _translate_smtp_error(error: str) -> str:
    """将 SMTP 错误信息转换为用户友好的中文提示"""
    error_lower = error.lower()

    # 认证相关错误
    if "username and password not accepted" in error_lower:
        return "用户名或密码错误，请检查 SMTP 凭据"
    if "authentication failed" in error_lower:
        return "认证失败，请检查用户名和密码"
    if "invalid credentials" in error_lower or "badcredentials" in error_lower:
        return "凭据无效，请检查用户名和密码"
    if "smtp auth extension is not supported" in error_lower:
        return "服务器不支持认证，请尝试使用 TLS 或 SSL 加密"

    # 连接相关错误
    if "connection refused" in error_lower:
        return "连接被拒绝，请检查服务器地址和端口"
    if "connection timed out" in error_lower or "timed out" in error_lower:
        return "连接超时，请检查网络或服务器地址"
    if "name or service not known" in error_lower or "getaddrinfo failed" in error_lower:
        return "无法解析服务器地址，请检查 SMTP 服务器地址"
    if "network is unreachable" in error_lower:
        return "网络不可达，请检查网络连接"

    # SSL/TLS 相关错误
    if "certificate verify failed" in error_lower:
        return "SSL 证书验证失败，请检查服务器证书或尝试其他加密方式"
    if "ssl" in error_lower and "wrong version" in error_lower:
        return "SSL 版本不匹配，请尝试其他加密方式"
    if "starttls" in error_lower:
        return "STARTTLS 握手失败，请检查加密设置"

    # 其他常见错误
    if "sender address rejected" in error_lower:
        return "发件人地址被拒绝，请检查发件人邮箱设置"
    if "relay access denied" in error_lower:
        return "中继访问被拒绝，请检查 SMTP 服务器配置"

    # 返回原始错误（简化格式）
    # 去掉错误码前缀，如 "(535, '5.7.8 ..."
    if error.startswith("(") and "'" in error:
        # 提取引号内的内容
        start = error.find("'") + 1
        end = error.rfind("'")
        if start > 0 and end > start:
            return error[start:end].replace("\\n", " ").strip()

    return error
