"""
web/tts_handler.py - TTS (Text-to-Speech) 模块
===============================================
基于 edge-tts 的语音合成服务，支持智能文本预处理（跳过代码块/公式）。
"""
from __future__ import annotations

import re
import hashlib
import asyncio
from pathlib import Path
from typing import Optional

import edge_tts

from core.logger import get_logger

logger = get_logger("myagent.tts")

# 默认中文语音
DEFAULT_VOICE = "zh-CN-XiaoxiaoNeural"
# 默认语速 (edge-tts 使用百分比字符串, "+0%" = 正常)
DEFAULT_SPEED = "+25%"
# 单次请求最大字符数 (edge-tts 限制约 5000, 保守取 3000)
MAX_CHUNK_LEN = 3000
# TTS 音频缓存目录
_CACHE_DIR: Optional[Path] = None


def _get_cache_dir() -> Path:
    global _CACHE_DIR
    if _CACHE_DIR is None:
        # 缓存到系统临时目录
        import tempfile
        _CACHE_DIR = Path(tempfile.gettempdir()) / "myagent_tts_cache"
        _CACHE_DIR.mkdir(parents=True, exist_ok=True)
    return _CACHE_DIR


def preprocess_for_tts(text: str) -> str:
    """
    智能文本预处理：去除代码块、公式、Markdown 语法等，
    仅保留适合语音播报的纯文本。
    """
    if not text or not text.strip():
        return ""

    # 1. 移除围栏代码块 (```...```)
    text = re.sub(r'```[\s\S]*?```', '', text)
    # 2. 移除行内代码 (`...`)
    text = re.sub(r'`[^`]+`', '', text)
    # 3. 移除数学公式 ($$...$$ 和 $...$)
    text = re.sub(r'\$\$[\s\S]*?\$\$', '', text)
    text = re.sub(r'(?<!\$)\$(?!\$)[^\$]+(?<!\$)\$(?!\$)', '', text)
    # 4. 移除图片语法, 替换为占位
    text = re.sub(r'!\[[^\]]*\]\([^)]+\)', '[图片]', text)
    # 5. 移除 Markdown 链接, 仅保留文本
    text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text)
    # 6. 移除加粗/斜体标记
    text = re.sub(r'(\*{1,3}|_{1,3})(.*?)\1', r'\2', text)
    # 7. 移除标题标记
    text = re.sub(r'^#{1,6}\s*', '', text, flags=re.MULTILINE)
    # 8. 移除表格分隔符
    text = re.sub(r'\|', ' ', text)
    text = re.sub(r'[-:]{2,}', ' ', text)
    # 9. 移除 HTML 标签 (残留)
    text = re.sub(r'<[^>]+>', '', text)
    # 10. 移除 emoji 和特殊符号（双重保险，前端已处理）
    text = re.sub(
        r'[\U0001F300-\U0001FAFF\U00002600-\U000027BF\U0000FE00-\U0000FE0F'
        r'\U0000200D\U000020E3\U00002300-\U000023FF\U00002B50-\U00002B55'
        r'\U0000203C-\U00003299]+'
        , '', text
    )
    text = re.sub(r'[✅❌⚠️🔄⏰🔒💻🔍📁🧠🌐🛠️👋🤖🎯💡🚀👍📊📝🔊💬📌✨✓✗→←↓↑⏹]+', '', text)
    # 11. 清理多余空白
    text = re.sub(r'\n{3,}', '\n\n', text)
    text = re.sub(r' {2,}', ' ', text)
    text = text.strip()

    return text


def _text_hash(text: str) -> str:
    """生成文本的短哈希, 用于缓存文件名"""
    return hashlib.md5(text.encode('utf-8')).hexdigest()[:16]


def _split_text(text: str, max_len: int = MAX_CHUNK_LEN) -> list[str]:
    """
    将长文本按句子边界分割成多个块。
    优先在句号、换行处分割, 避免在句子中间截断。
    """
    if len(text) <= max_len:
        return [text]

    chunks = []
    # 按换行和句子结尾分割
    segments = re.split(r'(?<=[。！？\n])', text)

    current = ""
    for seg in segments:
        if not seg.strip():
            continue
        if len(current) + len(seg) > max_len:
            if current:
                chunks.append(current.strip())
            current = seg
        else:
            current += seg

    if current.strip():
        chunks.append(current.strip())

    return chunks if chunks else [text[:max_len]]


async def synthesize(
    text: str,
    voice: str = DEFAULT_VOICE,
    speed: str = DEFAULT_SPEED,
) -> bytes:
    """
    将文本合成为 MP3 音频。

    Args:
        text: 要合成的文本 (已预处理)
        voice: edge-tts 语音名称
        speed: 语速 (如 "+0%", "-10%", "+20%")

    Returns:
        MP3 音频字节数据

    Raises:
        ValueError: 文本为空
        RuntimeError: TTS 合成失败
    """
    text = preprocess_for_tts(text)
    if not text:
        raise ValueError("预处理后文本为空, 无法合成语音")

    chunks = _split_text(text)
    all_audio = b""

    for i, chunk in enumerate(chunks):
        cache_key = f"{voice}_{speed}_{_text_hash(chunk)}.mp3"
        cache_path = _get_cache_dir() / cache_key

        # 检查缓存
        if cache_path.exists():
            logger.debug(f"TTS 缓存命中: {cache_key}")
            all_audio += cache_path.read_bytes()
            continue

        # 调用 edge-tts（最多重试 2 次）
        max_retries = 2
        for attempt in range(max_retries):
            try:
                communicate = edge_tts.Communicate(chunk, voice, rate=speed)
                audio_data = b""
                async for chunk_data in communicate.stream():
                    if chunk_data["type"] == "audio":
                        audio_data += chunk_data["data"]

                if not audio_data:
                    logger.warning(f"TTS 块 {i} 生成空音频 (重试 {attempt + 1}/{max_retries})")
                    if attempt < max_retries - 1:
                        await asyncio.sleep(0.5)
                        continue
                    else:
                        break

                # 写入缓存
                try:
                    cache_path.write_bytes(audio_data)
                except Exception as e:
                    logger.debug(f"TTS 缓存写入失败: {e}")

                all_audio += audio_data
                break
            except Exception as e:
                logger.warning(f"TTS 合成失败 (块 {i}, 重试 {attempt + 1}/{max_retries}): {e}")
                if attempt < max_retries - 1:
                    await asyncio.sleep(0.5)
                else:
                    logger.error(f"TTS 合成最终失败 (块 {i}): {e}")
                    # 不 raise，跳过这个块继续合成后面的
                    break

    if not all_audio:
        raise RuntimeError("语音合成结果为空")

    return all_audio


# 可用中文语音列表 (供前端选择)
AVAILABLE_VOICES = [
    {"id": "zh-CN-XiaoxiaoNeural", "name": "晓晓", "desc": "温柔女声 (默认)"},
    {"id": "zh-CN-YunxiNeural", "name": "云希", "desc": "阳光男声"},
    {"id": "zh-CN-YunjianNeural", "name": "云健", "desc": "成熟男声"},
    {"id": "zh-CN-XiaoyiNeural", "name": "晓依", "desc": "活泼女声"},
    {"id": "zh-CN-YunyangNeural", "name": "云扬", "desc": "新闻男声"},
    {"id": "zh-CN-XiaochenNeural", "name": "晓辰", "desc": "沉稳男声"},
    {"id": "zh-CN-XiaohanNeural", "name": "晓涵", "desc": "温柔女声2"},
    {"id": "zh-CN-XiaomoNeural", "name": "晓墨", "desc": "知性女声"},
    {"id": "zh-CN-XiaoqiuNeural", "name": "晓秋", "desc": "亲切女声"},
    {"id": "zh-CN-XiaoxuanNeural", "name": "晓萱", "desc": "甜美女声"},
    {"id": "zh-CN-XiaoruiNeural", "name": "晓瑞", "desc": "端庄女声"},
    {"id": "zh-CN-XiaoshuangNeural", "name": "晓双", "desc": "儿童女声"},
    {"id": "zh-CN-XiaoyanNeural", "name": "晓颜", "desc": "自然女声"},
    {"id": "zh-CN-XiaozhenNeural", "name": "晓甄", "desc": "甜美女声2"},
    {"id": "zh-TW-HsiaoChenNeural", "name": "晓辰(台湾)", "desc": "台湾腔女声"},
    {"id": "zh-TW-HsiaoYuNeural", "name": "晓雨(台湾)", "desc": "台湾腔女声2"},
    {"id": "zh-HK-HiuGaaiNeural", "name": "曉佳(粤语)", "desc": "粤语女声"},
    {"id": "zh-HK-HiuMaanNeural", "name": "曉曼(粤语)", "desc": "粤语女声2"},
]
