"""
邮件客户端工具模块

提供邮件发送功能，支持：
- QQ邮箱SMTP发送
- HTML格式邮件内容
- 多账户随机轮换
- 文件附件

配置环境变量（pwd为SMTP授权码，非邮箱登录密码）：
    export MAIL_ACCT="{email1}##{pwd1}^^{email2}##{pwd2}"

示例（仅支持QQ邮箱）：
    export MAIL_ACCT="{123@qq.com}##{smtp_pwd_123}^^{456@qq.com}##{smtp_pwd_456}"
"""
import os
import random
import logging
import smtplib
from typing import List, Optional, Union, Set

from email.header import Header
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

from xsdk.template import HtmlTemplate
from xsdk.file_util import FileUtil
from xsdk.xutil import Xutil

logger = logging.getLogger(__name__)


class MailAccountData:
    """
    邮箱账户配置类

    Attributes:
        user: 用户邮箱地址
        password: SMTP授权码（非邮箱登录密码）
        smtp_server: SMTP服务器地址
        smtp_port: SMTP服务器端口
    """

    def __init__(self, usr: str = "", pwd: str = ""):
        self.user = usr
        self.password = pwd
        self.smtp_server = "smtp.qq.com"
        self.smtp_port = 465

    def __str__(self) -> str:
        return str(self.__dict__)

    @staticmethod
    def get_default_account() -> "MailAccountData":
        """
        从环境变量获取默认邮箱账户（随机选择）

        Returns:
            MailAccountData实例
        """
        config_str = Xutil.get_env_param("MAIL_ACCT")
        if Xutil.is_empty(config_str):
            return MailAccountData()

        account_list = config_str.split("^^")
        if not account_list:
            return MailAccountData()

        default_account_list = []
        for account_info in account_list:
            if "##" not in account_info:
                continue
            usr, pwd = account_info.split("##", 1)
            default_account_list.append(MailAccountData(usr.strip(), pwd.strip()))

        if not default_account_list:
            return MailAccountData()

        idx = random.randint(0, len(default_account_list) - 1)
        return default_account_list[idx]


class MailContentData:
    """
    邮件内容数据类

    Attributes:
        title: 内容标题
        data: 具体内容（字符串、列表或字典列表）
        table_flag: 是否将data生成为HTML表格
    """

    def __init__(self, title: str, data: Union[str, List, Set], table_flag: bool = False):
        """
        Args:
            title: 内容描述
            data: 具体内容
            table_flag: 是否生成表格（如果为True，data必须是字典列表）
        """
        self.title = title
        self.data = data
        self.table_flag = table_flag

    @staticmethod
    def get_mail_content(mail_content_list: Union["MailContentData", List]) -> str:
        """
        将邮件内容数据转换为HTML格式

        Args:
            mail_content_list: MailContentData实例或列表

        Returns:
            HTML格式的邮件内容
        """
        if mail_content_list is None or (isinstance(mail_content_list, list) and len(mail_content_list) == 0):
            return HtmlTemplate.get_html("")

        if isinstance(mail_content_list, MailContentData):
            mail_content_list = [mail_content_list]

        if not isinstance(mail_content_list, list):
            return HtmlTemplate.get_html(str(mail_content_list))

        content = ""
        other_data_list = []

        for mail_content_data in mail_content_list:
            if not isinstance(mail_content_data, MailContentData):
                other_data_list.append(mail_content_data)
                continue

            content_div = ""
            title = f"<b>{mail_content_data.title}:</b>"
            data = mail_content_data.data
            table_flag = mail_content_data.table_flag

            content_div += f"</br>{title}<br>"

            if table_flag:
                table = HtmlTemplate.get_table(data)
                content_div += table
            else:
                if isinstance(data, (list, set)):
                    data_sorted = sorted(data)
                    for line in data_sorted:
                        content_div += f"{line}</br>"
                else:
                    content_div += f"{data}</br>"

            content += f"<div style='clear:both;'>{content_div}</div></br>"

        # 处理非MailContentData类型的数据
        for data in other_data_list:
            if isinstance(data, (set, list, tuple)):
                for line in data:
                    content += f"{line}</br>"
            else:
                content += f"{data}</br>"

        return HtmlTemplate.get_html(content)


class MailClient:
    """
    邮件客户端类

    提供邮件发送功能，支持HTML格式和附件。

    Example:
        >>> content = MailContentData("标题", "内容")
        >>> MailClient.send_mail_v2("邮件主题", "receiver@qq.com", [content])
    """

    @staticmethod
    def get_receiver_list(receiver_list: Union[str, List[str]], suffix: str = "@qq.com") -> List[str]:
        """
        处理收件人列表

        Args:
            receiver_list: 收件人列表（字符串用分号分隔，或列表）
            suffix: 默认邮箱后缀

        Returns:
            处理后的收件人列表
        """
        if isinstance(receiver_list, str):
            receiver_list = receiver_list.split(";")

        result = []
        for receiver in receiver_list:
            if Xutil.is_empty(receiver):
                continue
            receiver = receiver.strip()
            if suffix and not receiver.endswith(suffix) and not receiver.endswith(".com"):
                receiver = f"{receiver}{suffix}"
            result.append(receiver)

        return result

    @staticmethod
    def send_mail_v2(
        mail_title: str,
        mail_to: Union[str, List[str]],
        mail_content_list: Optional[List[MailContentData]] = None,
        attach_file_list: Optional[List[str]] = None,
        logger: logging.Logger = logger
    ) -> bool:
        """
        发送HTML格式邮件

        Args:
            mail_title: 邮件主题
            mail_to: 收件人（字符串或列表）
            mail_content_list: MailContentData列表
            attach_file_list: 附件文件路径列表
            logger: 日志记录器

        Returns:
            发送是否成功
        """
        try:
            mail_content = MailContentData.get_mail_content(mail_content_list)
            return MailClient.send_mail_smtp(mail_title, mail_to, mail_content, attach_file_list, logger)
        except Exception as e:
            logger.warning(f"Failed to send mail: {e}")
            logger.exception(e)
            return False

    @staticmethod
    def send_mail_smtp(
        mail_title: str,
        mail_to: Union[str, List[str]],
        mail_content: str,
        attach_file_list: Optional[List[str]] = None,
        logger: logging.Logger = logger
    ) -> bool:
        """
        使用SMTP发送邮件

        Args:
            mail_title: 邮件主题
            mail_to: 收件人
            mail_content: 邮件内容（HTML格式）
            attach_file_list: 附件文件路径列表
            logger: 日志记录器

        Returns:
            发送是否成功
        """
        receiver_list = MailClient.get_receiver_list(mail_to)

        mail_account = MailAccountData.get_default_account()
        message = MIMEMultipart()
        message['From'] = mail_account.user
        message['To'] = ",".join(receiver_list)
        message['Subject'] = Header(mail_title, 'utf-8')

        # 邮件正文
        message.attach(MIMEText(mail_content, 'HTML', 'utf-8'))

        # 添加附件
        if attach_file_list:
            for attach_file in attach_file_list:
                if os.path.exists(attach_file):
                    attach_file_name = FileUtil.get_file_name(attach_file)
                    with open(attach_file, 'rb') as f:
                        att = MIMEText(f.read(), 'base64', 'utf-8')
                    att["Content-Type"] = 'application/octet-stream'
                    att["Content-Disposition"] = f'attachment; filename={attach_file_name}'
                    message.attach(att)

        try:
            smtp = smtplib.SMTP_SSL(mail_account.smtp_server, mail_account.smtp_port)
            smtp.set_debuglevel(0)
            smtp.ehlo(mail_account.smtp_server)
            smtp.login(mail_account.user, mail_account.password)
            smtp.sendmail(mail_account.user, receiver_list, message.as_string())
            smtp.quit()
            logger.info("Mail sent successfully")
            return True
        except smtplib.SMTPException as e:
            logger.warning("Failed to send mail via SMTP")
            logger.exception(e)
            return False


if __name__ == "__main__":
    test_title = "TEST"
    test_to = "1955463634@qq.com"
    test_content = "TEST CONTENT"
    MailClient.send_mail_smtp(test_title, test_to, test_content, attach_file_list=[])
