"""
工具函数模块
常用工具函数集合
"""
import re
import json
import random
import string
import hashlib
import base64
from typing import Optional, List, Dict, Any, Union, Tuple
from datetime import datetime, timedelta
from pathlib import Path
from loguru import logger


# ==================== 字符串工具 ====================

def random_string(length: int = 10, chars: str = None) -> str:
    """
    生成随机字符串
    
    Args:
        length: 长度
        chars: 字符集，默认字母数字
        
    Returns:
        str: 随机字符串
    """
    if chars is None:
        chars = string.ascii_letters + string.digits
    return ''.join(random.choice(chars) for _ in range(length))


def random_email(domain: str = "test.com") -> str:
    """生成随机邮箱"""
    return f"{random_string(8).lower()}@{domain}"


def random_phone(country_code: str = "86") -> str:
    """生成随机手机号"""
    if country_code == "86":
        # 中国手机号
        prefixes = ["130", "131", "132", "133", "135", "136", "137", "138", "139",
                   "150", "151", "152", "153", "155", "156", "157", "158", "159",
                   "180", "181", "182", "183", "184", "185", "186", "187", "188", "189"]
        return random.choice(prefixes) + ''.join(random.choice(string.digits) for _ in range(8))
    else:
        return '+' + country_code + ''.join(random.choice(string.digits) for _ in range(10))


def random_password(length: int = 12, include_special: bool = True) -> str:
    """生成随机密码"""
    chars = string.ascii_letters + string.digits
    if include_special:
        chars += "!@#$%^&*"
    
    # 确保包含各种类型字符
    password = [
        random.choice(string.ascii_uppercase),
        random.choice(string.ascii_lowercase),
        random.choice(string.digits),
    ]
    
    if include_special:
        password.append(random.choice("!@#$%^&*"))
    
    # 填充剩余长度
    password += [random.choice(chars) for _ in range(length - len(password))]
    random.shuffle(password)
    
    return ''.join(password)


def mask_sensitive(text: str, show_len: int = 4, mask_char: str = "*") -> str:
    """
    敏感信息脱敏
    
    Args:
        text: 原文
        show_len: 显示长度
        mask_char: 掩码字符
        
    Returns:
        str: 脱敏后的文本
    """
    if len(text) <= show_len * 2:
        return mask_char * len(text)
    
    return text[:show_len] + mask_char * (len(text) - show_len * 2) + text[-show_len:]


def truncate(text: str, max_len: int = 100, suffix: str = "...") -> str:
    """截断文本"""
    if len(text) <= max_len:
        return text
    return text[:max_len - len(suffix)] + suffix


def extract_numbers(text: str) -> List[str]:
    """提取文本中的数字"""
    return re.findall(r'\d+\.?\d*', text)


def extract_urls(text: str) -> List[str]:
    """提取文本中的URL"""
    pattern = r'https?://[^\s<>"{}|\\^`\[\]]+'
    return re.findall(pattern, text)


def extract_emails(text: str) -> List[str]:
    """提取文本中的邮箱"""
    pattern = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
    return re.findall(pattern, text)


def extract_phones(text: str) -> List[str]:
    """提取文本中的电话号码"""
    pattern = r'1[3-9]\d{9}'
    return re.findall(pattern, text)


def to_snake_case(text: str) -> str:
    """转换为蛇形命名"""
    s1 = re.sub(r'(.)([A-Z][a-z]+)', r'\1_\2', text)
    return re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', s1).lower()


def to_camel_case(text: str) -> str:
    """转换为驼峰命名"""
    components = text.split('_')
    return components[0] + ''.join(x.title() for x in components[1:])


def to_kebab_case(text: str) -> str:
    """转换为短横线命名"""
    return to_snake_case(text).replace('_', '-')


# ==================== 编码工具 ====================

def base64_encode(text: str) -> str:
    """Base64编码"""
    return base64.b64encode(text.encode()).decode()


def base64_decode(encoded: str) -> str:
    """Base64解码"""
    return base64.b64decode(encoded.encode()).decode()


def md5_hash(text: str) -> str:
    """MD5哈希"""
    return hashlib.md5(text.encode()).hexdigest()


def sha256_hash(text: str) -> str:
    """SHA256哈希"""
    return hashlib.sha256(text.encode()).hexdigest()


def url_encode(text: str) -> str:
    """URL编码"""
    from urllib.parse import quote
    return quote(text)


def url_decode(encoded: str) -> str:
    """URL解码"""
    from urllib.parse import unquote
    return unquote(encoded)


# ==================== 时间工具 ====================

def now_str(format: str = "%Y-%m-%d %H:%M:%S") -> str:
    """当前时间字符串"""
    return datetime.now().strftime(format)


def timestamp() -> int:
    """当前时间戳（秒）"""
    return int(datetime.now().timestamp())


def timestamp_ms() -> int:
    """当前时间戳（毫秒）"""
    return int(datetime.now().timestamp() * 1000)


def parse_datetime(text: str, format: str = None) -> datetime:
    """解析日期时间"""
    if format:
        return datetime.strptime(text, format)
    
    # 尝试多种格式
    formats = [
        "%Y-%m-%d %H:%M:%S",
        "%Y-%m-%d %H:%M",
        "%Y-%m-%d",
        "%Y/%m/%d %H:%M:%S",
        "%Y/%m/%d",
        "%d/%m/%Y",
    ]
    
    for fmt in formats:
        try:
            return datetime.strptime(text, fmt)
        except ValueError:
            continue
    
    raise ValueError(f"无法解析日期时间: {text}")


def time_ago(dt: datetime) -> str:
    """人性化时间显示"""
    now = datetime.now()
    diff = now - dt
    
    if diff.seconds < 60:
        return "刚刚"
    elif diff.seconds < 3600:
        return f"{diff.seconds // 60}分钟前"
    elif diff.seconds < 86400:
        return f"{diff.seconds // 3600}小时前"
    elif diff.days < 30:
        return f"{diff.days}天前"
    elif diff.days < 365:
        return f"{diff.days // 30}个月前"
    else:
        return f"{diff.days // 365}年前"


# ==================== 文件工具 ====================

def ensure_dir(path: str):
    """确保目录存在"""
    Path(path).mkdir(parents=True, exist_ok=True)


def read_file(path: str, encoding: str = "utf-8") -> str:
    """读取文件"""
    return Path(path).read_text(encoding=encoding)


def write_file(path: str, content: str, encoding: str = "utf-8"):
    """写入文件"""
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    Path(path).write_text(content, encoding=encoding)


def read_json(path: str) -> Any:
    """读取JSON文件"""
    with open(path, 'r', encoding='utf-8') as f:
        return json.load(f)


def write_json(path: str, data: Any, indent: int = 2):
    """写入JSON文件"""
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    with open(path, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=indent)


def file_size(path: str) -> int:
    """文件大小（字节）"""
    return Path(path).stat().st_size


def file_size_human(path: str) -> str:
    """人性化文件大小"""
    size = file_size(path)
    
    for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
        if size < 1024:
            return f"{size:.2f} {unit}"
        size /= 1024
    
    return f"{size:.2f} PB"


# ==================== 数据验证工具 ====================

def is_valid_email(email: str) -> bool:
    """验证邮箱格式"""
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))


def is_valid_phone(phone: str) -> bool:
    """验证手机号格式"""
    pattern = r'^1[3-9]\d{9}$'
    return bool(re.match(pattern, phone))


def is_valid_url(url: str) -> bool:
    """验证URL格式"""
    pattern = r'^https?://[^\s<>"{}|\\^`\[\]]+$'
    return bool(re.match(pattern, url))


def is_valid_id_card(id_card: str) -> bool:
    """验证身份证号格式（简单验证）"""
    pattern = r'^\d{17}[\dXx]$'
    return bool(re.match(pattern, id_card))


def is_json(text: str) -> bool:
    """验证是否为JSON"""
    try:
        json.loads(text)
        return True
    except:
        return False


# ==================== 数据转换工具 ====================

def deep_get(data: Dict, path: str, default: Any = None) -> Any:
    """
    深度获取字典值
    
    Args:
        data: 字典数据
        path: 路径，如 "user.profile.name"
        default: 默认值
        
    Returns:
        Any: 值
    """
    keys = path.split('.')
    result = data
    
    for key in keys:
        if isinstance(result, dict) and key in result:
            result = result[key]
        else:
            return default
    
    return result


def deep_set(data: Dict, path: str, value: Any):
    """
    深度设置字典值
    
    Args:
        data: 字典数据
        path: 路径
        value: 值
    """
    keys = path.split('.')
    current = data
    
    for key in keys[:-1]:
        if key not in current:
            current[key] = {}
        current = current[key]
    
    current[keys[-1]] = value


def flatten_dict(data: Dict, separator: str = '.') -> Dict:
    """展平嵌套字典"""
    result = {}
    
    def _flatten(obj, prefix=''):
        if isinstance(obj, dict):
            for key, value in obj.items():
                new_key = f"{prefix}{separator}{key}" if prefix else key
                _flatten(value, new_key)
        else:
            result[prefix] = obj
    
    _flatten(data)
    return result


def unflatten_dict(data: Dict, separator: str = '.') -> Dict:
    """还原展平的字典"""
    result = {}
    
    for key, value in data.items():
        deep_set(result, key, value)
    
    return result


def list_to_dict(lst: List, key_field: str) -> Dict:
    """
    列表转字典
    
    Args:
        lst: 列表
        key_field: 作为键的字段
        
    Returns:
        Dict: 字典
    """
    return {item[key_field]: item for item in lst}


def group_by(lst: List, key_field: str) -> Dict[str, List]:
    """
    按字段分组
    
    Args:
        lst: 列表
        key_field: 分组字段
        
    Returns:
        Dict: 分组结果
    """
    result = {}
    
    for item in lst:
        key = item.get(key_field)
        if key not in result:
            result[key] = []
        result[key].append(item)
    
    return result


# ==================== 重试和等待工具 ====================

async def wait_for(
    condition: callable,
    timeout: float = 30.0,
    interval: float = 0.5,
) -> bool:
    """
    等待条件满足
    
    Args:
        condition: 条件函数
        timeout: 超时时间（秒）
        interval: 检查间隔（秒）
        
    Returns:
        bool: 是否满足
    """
    import asyncio
    
    start = datetime.now()
    
    while (datetime.now() - start).total_seconds() < timeout:
        try:
            if asyncio.iscoroutinefunction(condition):
                if await condition():
                    return True
            else:
                if condition():
                    return True
        except:
            pass
        
        await asyncio.sleep(interval)
    
    return False


async def retry(
    func: callable,
    max_retries: int = 3,
    delay: float = 1.0,
    backoff: float = 2.0,
) -> Any:
    """
    重试执行
    
    Args:
        func: 要执行的函数
        max_retries: 最大重试次数
        delay: 初始延迟
        backoff: 退避因子
        
    Returns:
        Any: 执行结果
    """
    import asyncio
    
    last_error = None
    current_delay = delay
    
    for attempt in range(max_retries):
        try:
            if asyncio.iscoroutinefunction(func):
                return await func()
            else:
                return func()
        except Exception as e:
            last_error = e
            logger.warning(f"重试 {attempt + 1}/{max_retries}: {e}")
            
            if attempt < max_retries - 1:
                await asyncio.sleep(current_delay)
                current_delay *= backoff
    
    raise last_error


# ==================== 装饰器工具 ====================

def timer(func):
    """计时装饰器"""
    import time
    import functools
    
    @functools.wraps(func)
    async def async_wrapper(*args, **kwargs):
        start = time.time()
        result = await func(*args, **kwargs)
        elapsed = time.time() - start
        logger.info(f"{func.__name__} 执行耗时: {elapsed:.3f}s")
        return result
    
    @functools.wraps(func)
    def sync_wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        logger.info(f"{func.__name__} 执行耗时: {elapsed:.3f}s")
        return result
    
    import asyncio
    if asyncio.iscoroutinefunction(func):
        return async_wrapper
    return sync_wrapper


def catch_errors(default=None):
    """错误捕获装饰器"""
    import functools
    
    def decorator(func):
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs):
            try:
                return await func(*args, **kwargs)
            except Exception as e:
                logger.error(f"{func.__name__} 执行失败: {e}")
                return default
        
        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                logger.error(f"{func.__name__} 执行失败: {e}")
                return default
        
        import asyncio
        if asyncio.iscoroutinefunction(func):
            return async_wrapper
        return sync_wrapper
    
    return decorator
