# coding=utf-8
"""
######################################################
@Description: common utils
@Author: yuanyang.li
@Date: 2025-11-28
######################################################
"""
import datetime
import hashlib
import os
import subprocess
import sys
import logging
import better_exceptions
import shortuuid

RECURSIVE_CREATE_DIR = lambda dp: os.makedirs(dp) or dp if not os.path.exists(dp) else dp  # ---> mkdir -p

def get_current_time():
    return datetime.datetime.now().strftime('%Y-%m-%d_%H_%M_%S')

######################################################

def func_example(path,header,use_cols):
    """
    @Description: Run a Flask Service
    @Args:
        path(str): run path
        header(int): header name
        use_cols(list): target clos
    @Returns:
        List: result list
        Examples:
                ["rst","rst_1","rst_2"]
    """
    pass
# print(func_example.__doc__)

def better_show_exception(print_type="print"):
    """
    @Description:
        Display stack information more effectively
    @Args:
        print_type(str): print type
    @Returns:
        None
    """
    e_type, value, e_traceback = sys.exc_info()
    msg = ''
    rst = better_exceptions.format_exception(e_type, value, e_traceback)
    for i in rst:
        msg += i
        msg += '\n'
    if print_type == "print":
        print(msg)
    else:
        logging.error(msg)


def human_duration(seconds: int) -> str:
    """
    @Description:
        Reverse seconds to a human read string,now support to 'year' unit
    @Args:
        seconds(int): need reverse seconds
    @Returns:
        Str: reversed result
        Examples:
                '3h 15m 34s'
    """
    if seconds == 0:
        return "0s"

    units = [
        ("year", 365 * 24 * 3600),
        ("day", 24 * 3600),
        ("h", 3600),
        ("m", 60),
        ("s", 1)
    ]

    parts = []
    remainder = abs(seconds)
    for name, count in units:
        if remainder >= count:
            num = remainder // count
            parts.append(f"{num}{name}")
            remainder -= num * count

    sign = "-" if seconds < 0 else ""
    return sign + " ".join(parts)

def take_screenshot(f_path,monitor_index=0):
    """
    @Description:
        Save a screenshot
    @Args:
        f_path(str): screenshot save path
        monitor_index(int): target monitor screenshot, -1 all save all
    @Returns:
        Bool: saved result
        Examples:
                True/False
    """
    import mss
    from mss import tools
    #  多屏截图
    if monitor_index == -1:
        with mss.mss() as sct:
            # 获取所有屏幕合成的整张图
            img = sct.grab(sct.monitors[0])  # [0] 表示虚拟桌面即将多个显示器截图合并成一张，[1]为第一台显示器截图，[2]为第二台显示器截图
            mss.tools.to_png(img.rgb, img.size, output=f_path)
            return True
    # 指定屏幕截图
    else:
        with mss.mss() as sct:
            # 1. 查看并打印所有显示器信息
            print("可用的显示器：")
            for monitor_id, monitor_info in enumerate(sct.monitors):
                print(f"显示器 {monitor_id}: {monitor_info}")
            # 2. 假设你要截取编号为1的显示器（通常是扩展屏）
            target_monitor_number = monitor_index
            # 检查目标显示器是否存在
            if target_monitor_number < len(sct.monitors):
                # 获取该显示器的信息字典
                monitor = sct.monitors[target_monitor_number]

                # 使用grab方法截取该显示器
                screenshot = sct.grab(monitor)

                # 保存截图（mss.tools.to_png很方便）
                output_filename = f_path
                tools.to_png(screenshot.rgb, screenshot.size, output=output_filename)
                print(f"显示器[{target_monitor_number}]截图已保存为: {output_filename}")
                return True
            else:
                print(f"错误：不存在编号为 {target_monitor_number} 的显示器。")
                return False

def calculate_string_md5(input_string):
    """
    @Description:
        Calculate the MD5 of a string
    @Args:
        f_path(str): screenshot save path
    @Returns:
        Bool: saved result
        Examples:
                '098F6BCD4621D373CADE4E832627B4F6'
    """
    md5 = hashlib.md5()
    b = input_string.encode(encoding='utf-8')
    md5.update(b)
    str_md5 = md5.hexdigest()
    return str_md5

def run_shell(cmd):
    rst_code, rst_msg = subprocess.getstatusoutput(cmd)
    if rst_code == 0:
        return rst_msg
    else:
        logging.error("shell cmd run failed and error code is : {},detail:{}".format(rst_code, rst_msg))
        return rst_msg


def run_shell_with_map(cmd):
    """
    @Description:
        Run shell cmd
    @Args:
        cmd(str): run cmd
    @Returns:
        dict: run result
        Examples:
                {"error": False, "result": "XXXX", "code": 0}
    """
    logging.debug("run cmd:{}".format(cmd))
    rst_code, rst_msg = subprocess.getstatusoutput(cmd)
    if rst_code == 0:
        rst_error = False
    else:
        subprocess.getstatusoutput("echo  {}  >> /tmp/error_cmd.log".format(cmd))
        rst_error = True
    return {"error": rst_error, "result": rst_msg, "code": rst_code}


def upload_image_to_ftp(ip, username, password, local_path, ftp_path):
    """
    @Description: 上传图片到 FTP 服务器

    @Args:
        ip (str): FTP 服务器IP地址
        username (str): FTP 用户名
        password (str): FTP 密码
        local_path (str): 本地图片路径（绝对路径）
        ftp_path (str): FTP 目标路径（如 '/images/'）

    @Return::
        str: 上传后的完整 FTP 路径（如 'ftp://192.168.1.100/images/photo.jpg'）

    @Exception:
        IOError: 如果本地文件不存在
        Exception: FTP 相关错误

    @Example:
        try:
        ftp_url = upload_image_to_ftp(
            ip="192.168.81.47",
            username="ftp_test",
            password="ftp_test",
            local_path=r"D:\2.png",
            ftp_path="/auto_test/img/test"
        )
        print(f"图片上传成功，路径: {ftp_url}")
    except Exception as e:
        print(f"错误: {str(e)}")

    """

    from ftplib import FTP
    # 验证本地文件是否存在
    if not os.path.isfile(local_path):
        raise IOError(f"本地文件不存在: {local_path}")

    # 获取文件名

    filename = os.path.basename(local_path)
    # print(f"ftp://{ip}{ftp_path.rstrip('/')}/{filename}")
    try:
        # 连接 FTP 服务器
        with FTP(ip) as ftp:
            ftp.login(user=username, passwd=password)

            # 切换到目标目录（如果不存在则创建）
            try:
                ftp.cwd(ftp_path).encode('utf-8')
            except:
                # 递归创建目录
                for dir_part in ftp_path.split('/'):
                    if dir_part:
                        try:
                            ftp.cwd(dir_part).encode('utf-8')
                        except:
                            ftp.mkd(dir_part).encode('utf-8')
                            ftp.cwd(dir_part).encode('utf-8')

            # 上传文件（二进制模式）
            with open(local_path, 'rb') as file:
                ftp.storbinary(f'STOR {filename}', file)

    except Exception as e:
        raise Exception(f"FTP 上传失败: {str(e)}")

    # 返回完整的 FTP 路径

    return f"ftp://{ip}{ftp_path.rstrip('/')}/{filename}"


def get_root_path():
    """
        获取测试根目录路径
    """
    curren_path = os.path.dirname(os.path.abspath(__file__))
    root_path = os.path.dirname(os.path.dirname(curren_path))
    print(f"根目录是:{root_path}")

    return root_path

def load_tomli(file_path):
    """
        @Description: 读取toml文件数据
        @Args:
            file_path(str): toml文件路径
        @Returns:
            dict: toml数据
    """
    import tomli
    with open(file_path, "rb") as f:  # 同样需要二进制模式
        config = tomli.load(f)
        return config

def get_config_data(get_type='global'):
    """
        @Description: 读取指定配置数据(conf/*.toml)
        @Args:
            get_type(str): 指定的toml文件名
        @Return:
            dict: toml数据
    """
    file_name = f"{get_type}.toml"
    file_path = os.path.join(get_root_path(),'conf',file_name)
    return load_tomli(file_path)

def send_check_img(img_path, img_rst,f_name=None):
    """
        @Description: 上传检查后的文件截图
        @Args:
            img_path(str): 截图文件的本地存储地址
            img_rst(str): 截图的检测结果: "normal"/"error"
            f_name(str): 保存的文件夹名,未给的情况会用uuid生成
        @Return:
            Boolean: 上传结果
    img_rst:
    """
    try:
        if not f_name:
            f_name = shortuuid.uuid()
        conf_data = get_config_data()
        ftp_url = upload_image_to_ftp(
            ip=conf_data['ftp_server']['ip'],
            username=conf_data['ftp_server']['user'],
            password=conf_data['ftp_server']['password'],
            local_path=img_path,
            ftp_path=f"{conf_data['img_server']['img_upload_path']}/{img_rst}/{f_name}"
        )
        logging.info(f"图片上传成功，路径: {ftp_url}")
        return True
    except Exception as e:
        logging.error(f"图片上传错误: {str(e)}")
        return False
