# -*- coding: utf-8 -*-
import base64
import hashlib
import json
import re
import types
import uuid

from typing import Any, Dict, List, Type, Union, Tuple, get_args, get_origin
from ddans.common.type import T, Callback, DataSizeResult


class NHook:

    @staticmethod
    def safe_func(func: Callback, *args, **kwargs):
        """_summary_

        Args:
            func (Callable[..., Any]): 方法函数

        Returns:
            Any: 返回值
        """
        result = None
        try:
            result = func(*args, **kwargs)
        except Exception:
            result = None
        finally:
            return result

    @staticmethod
    def buffer_sha512(buffer: types, b64encode=True):
        try:
            if not NHook.isvalid(buffer):
                return None
            sha512 = hashlib.sha512()
            sha512.update(buffer)
            if b64encode:
                digest = sha512.digest()
                return base64.b64encode(digest).decode('utf-8')
            else:
                return sha512.hexdigest()
        except Exception:
            return None

    @staticmethod
    def buffer_md5(buffer: types, b64encode=False):
        try:
            if not NHook.isvalid(buffer):
                return None
            md5 = hashlib.md5()
            md5.update(buffer)
            if b64encode:
                digest = md5.digest()
                return base64.b64encode(digest).decode('utf-8')
            else:
                return md5.hexdigest()
        except Exception:
            return None

    @staticmethod
    def buffer_utf8(buffer: Union[bytes, bytearray, str]):
        try:
            if not NHook.isvalid(buffer, (bytes, bytearray, str), True):
                return None
            if isinstance(buffer, str):
                return buffer
            return buffer.decode('utf-8')
        except Exception:
            return None

    @staticmethod
    def buffer_hex(buffer: Union[bytes, bytearray, str]):
        try:
            if not NHook.isvalid(buffer, (bytes, bytearray, str), True):
                return None
            if isinstance(buffer, str):
                return buffer.encode('utf-8').hex()
            else:
                return buffer.hex()
        except Exception as e:
            print(f'{e}')
            return None

    @staticmethod
    def base64_to_hex(base64_str: str):
        try:
            if not NHook.isvalid(base64_str):
                return None
            buffer = base64.b64decode(base64_str)
            return buffer.hex()
        except Exception:
            return None

    @staticmethod
    def hex_to_base64(hex_str: str) -> str:
        try:
            if not NHook.isvalid(hex_str):
                return None
            # 将十六进制字符串转换为字节
            buffer = bytes.fromhex(hex_str)

            # 将字节数据编码为Base64字符串
            return base64.b64encode(buffer).decode('utf-8')
        except Exception:
            return None

    @staticmethod
    def from_hex(hex_str: str, default=b''):
        try:
            if not NHook.isvalid(hex_str):
                return default
            return bytes.fromhex(hex_str)
        except Exception:
            return default

    @staticmethod
    def from_base64(text: str, default=b''):
        try:
            if not isinstance(text, str):
                return default
            return base64.b64decode(text)
        except Exception:
            return default

    @staticmethod
    def to_base64(buffer: Union[bytes, bytearray, str], default=''):
        try:
            if not NHook.isvalid(buffer, (bytes, bytearray, str), True):
                return default

            if isinstance(buffer, str):
                # 将字符串转换为字节
                buffer = buffer.encode('utf-8')

            # 将字节或字节数组进行Base64编码
            base64_encoded = base64.b64encode(buffer)

            # 将Base64编码的字节转换为字符串
            return base64_encoded.decode('utf-8')
        except Exception:
            return default

    @staticmethod
    def isvalid_base64(base64_str: str) -> bool:
        # 检查字符串是否只包含Base64字符和等号（用于填充）
        base64_pattern = re.compile(r'^[A-Za-z0-9+/]*={0,2}$')

        # 检查长度是否是4的倍数
        if len(base64_str) % 4 != 0:
            return False

        # 检查是否符合Base64字符集
        if not base64_pattern.match(base64_str):
            return False

        try:
            # 尝试解码Base64字符串
            base64.b64decode(base64_str, validate=True)
            return True
        except Exception:
            return False

    @staticmethod
    def convert_value(value, data_type: type = str, default=None):
        try:
            return data_type(value)
        except (ValueError, TypeError):
            # 如果转换失败，则返回 None
            return default

    @staticmethod
    def isvalid_str(value):
        return NHook.isvalid(value, str)

    @staticmethod
    def isvalid(value, value_type: type = None, check_len=False):
        if value is None:
            return False
        if value_type is not None and not isinstance(value, value_type):
            return False
        elif isinstance(value, int):
            return not isinstance(value, bool)
        elif isinstance(value, str):
            return len(value) > 0
        elif isinstance(value, bytes):
            return len(value) > 0

        if check_len:
            return len(value) > 0
        return True

    @staticmethod
    def to_json(text: str | bytes):
        return NHook.safe_func(json.loads, text)

    @staticmethod
    def json_dumps(obj: Any, default=None, indent: int | str = None):
        if obj is None:
            return default
        return json.dumps(obj, ensure_ascii=False,
                          indent=indent).encode('utf-8')

    @staticmethod
    def json_str(obj: Any, default=None, indent: int | str = None):
        if obj is None:
            return default
        return json.dumps(obj, ensure_ascii=False, indent=indent)

    @staticmethod
    def json_format(txt: str = "", indent: int = 2, keep_raw=False):
        try:
            data = json.loads(txt)
            content = json.dumps(data, ensure_ascii=False, indent=indent)
            return content
        except ValueError:
            return txt if keep_raw else ""

    @staticmethod
    def to_list(*args):
        arg_list = []
        for arg in args:
            if isinstance(arg, (list, tuple)):
                arg_list.extend(arg)
            else:
                arg_list.append(arg)
        return arg_list

    @staticmethod
    def to_string(data: dict):
        return NHook.safe_func(str, data)

    @staticmethod
    def to_int(s: str, default: int = 0) -> int:
        try:
            return int(s)
        except ValueError:
            return default

    @staticmethod
    def index(ele: T, lst: List[T] = []) -> int:
        try:
            return lst.index(ele)
        except ValueError:
            return -1

    @staticmethod
    def join_args(*args):
        return " ".join(args)

    @staticmethod
    def to(func: Callback[T], *args, **kwargs) ->\
            Union[Tuple[Exception, None], Tuple[T, None]]:
        try:
            result = func(*args, **kwargs)
            return None, result
        except Exception as e:
            return e, None

    @staticmethod
    def guid() -> str:
        """ 获取guid

        Returns:
            str: guid
        """
        return str(uuid.uuid4())

    @staticmethod
    def uuid32() -> str:
        return NHook.guid().replace("-", "")

    @staticmethod
    def unique(source: list = []):
        """ 列表去重

        Args:
            source (list, optional): 列表. Defaults to [].

        Returns:
            list: 去重后的列表
        """
        return list(dict.fromkeys(source))

    @staticmethod
    def isvalid_version(version):
        """ 版本号是否有效

        Args:
            version (str): 版本号, 如 1.0.0

        Returns:
            bool: 是 | 否
        """
        # 版本号只能包含数字和点，并且点不能在开头或结尾，也不能连续出现
        if not version or version[0] == '.' or version[
                -1] == '.' or '..' in version:
            return False

        parts = version.split('.')

        for part in parts:
            # 检查每个部分是否都是数字
            if not part.isdigit():
                return False

        return True

    @staticmethod
    def compare_versions(version1: str, version2: str):
        """ 版本号对比, 格式如: 1.0.0

        Args:
            version1 (str): 版本号1
            version2 (str): 版本号2

        Returns:
            int: -1 | 0 | 1;
        """
        # 先进行合法性检查
        if not NHook.isvalid_version(version1) or not NHook.isvalid_version(
                version2):
            raise ValueError("Invalid version format")

        # 分割版本号成列表
        v1_parts = version1.split('.')
        v2_parts = version2.split('.')

        # 计算最大长度
        max_length = max(len(v1_parts), len(v2_parts))

        # 补齐版本号部分
        v1_parts += ['0'] * (max_length - len(v1_parts))
        v2_parts += ['0'] * (max_length - len(v2_parts))

        # 逐一比较版本号的部分
        for i in range(max_length):
            v1_part = int(v1_parts[i])
            v2_part = int(v2_parts[i])
            if v1_part > v2_part:
                return 1
            elif v1_part < v2_part:
                return -1

        # 如果所有部分都相同
        return 0

    @staticmethod
    def number_to_chinese(n):

        # 检查输入是否为整数
        if not isinstance(n, int):
            return ''

        # 检查整数是否为非负数
        if n < 0:
            return ''

        # 定义中文数字字符
        num_map = {
            '0': '零',
            '1': '一',
            '2': '二',
            '3': '三',
            '4': '四',
            '5': '五',
            '6': '六',
            '7': '七',
            '8': '八',
            '9': '九'
        }

        # 定义中文单位字符
        unit_map = ['', '十', '百', '千', '万', '十万', '百万', '千万', '亿']

        # 将数字转为字符串并反转以便从低位到高位处理
        num_str = str(n)[::-1]
        chinese_str = ''

        # 遍历数字字符串
        for i in range(len(num_str)):
            digit = num_str[i]
            if digit != '0':
                chinese_str = num_map[digit] + unit_map[i] + chinese_str
            else:
                if i == 0 or num_str[i - 1] != '0':
                    chinese_str = num_map[digit] + chinese_str

        # 去掉末尾的'零'
        if chinese_str.endswith('零'):
            chinese_str = chinese_str[:-1]

        # 处理特殊情况：去掉'一十'的'一'
        if chinese_str.startswith('一十'):
            chinese_str = chinese_str[1:]

        return chinese_str

    @staticmethod
    def is_instance(obj: Any, typ: Type[T]) -> bool:
        origin = get_origin(typ) or typ

        if origin in {dict, Dict}:
            if not isinstance(obj, dict):
                return False

            # Extract key and value types if available, otherwise use Any
            args = get_args(typ)
            key_type, value_type = args if len(args) == 2 else (Any, Any)

            for key, value in obj.items():
                if key_type is not Any and not NHook.is_instance(
                        key, key_type):
                    return False
                if value_type is not Any and not NHook.is_instance(
                        value, value_type):
                    return False
            return True

        # Handle simple built-in types and other generic types
        if isinstance(origin, type):
            if typ == int and isinstance(obj, bool):
                return False
            return isinstance(obj, origin)
        return False

    @staticmethod
    def calc_data_size(data: int) -> DataSizeResult:
        units = [
            {
                'name': 'gb',
                'divisor': 1 << 30
            },  # 1 GB = 2^30 B
            {
                'name': 'mb',
                'divisor': 1 << 20
            },  # 1 MB = 2^20 B
            {
                'name': 'kb',
                'divisor': 1 << 10
            },  # 1 KB = 2^10 B
            {
                'name': 'b',
                'divisor': 1
            },  # 1 B = 1 B
        ]

        result: DataSizeResult = {
            'total': data,
            'desc': '',
            **{
                unit['name']: 0
                for unit in units
            }
        }

        # 计算每个单位的数量
        for unit in units:
            result[unit['name']] = data // unit['divisor']
            data %= unit['divisor']

        # 生成简洁的描述
        if result['gb']:
            result['desc'] = f"{result['gb'] + result['mb'] / 1024:.2f}GB"
        elif result['mb']:
            result['desc'] = f"{result['mb'] + result['kb'] / 1024:.2f}MB"
        elif result['kb']:
            result['desc'] = f"{result['kb'] + result['b'] / 1024:.2f}KB"
        else:
            result['desc'] = f"{result['b']}B"

        return result

    @staticmethod
    def calc_in_format(value, denominator):
        if not denominator:
            return value // 1
        return value // denominator  # 除以1000并向下取整

    @staticmethod
    def get_value_if(condition, true_value, false_value=None):
        # 处理条件（支持函数或静态值）
        cond_result = condition() if callable(condition) else condition
        # 根据条件返回对应的值（支持函数或静态值）
        if cond_result:
            return true_value() if callable(true_value) else true_value
        else:
            return false_value() if callable(false_value) else false_value

    @staticmethod
    def get_value(obj, key: str, default=""):
        try:
            return obj.get(key, default)
        except Exception:
            return default
