from lxml import etree


from typing import Any, List, Dict
import numpy as np


import logging

from modules.data import styles_mgr
from modules.speaker import speaker_mgr

import random


logger = logging.getLogger(__name__)


def expand_spk(attrs: dict):
    input_spk = attrs.get("spk", "")
    if isinstance(input_spk, int):
        return
    if isinstance(input_spk, str) and input_spk.isdigit():
        attrs.update({"spk": int(input_spk)})
        return
    try:
        speaker = speaker_mgr.get_speaker(input_spk)
        attrs.update({"spk": speaker})
    except Exception as e:
        logger.error(f"apply style failed, {e}")


def expand_style(attrs: dict):
    if attrs.get("style", "") != "":
        try:
            params = styles_mgr.find_params_by_name(str(attrs["style"]))
            attrs.update(params)
        except Exception as e:
            logger.error(f"apply style failed, {e}")


def merge_prompt(attrs: dict, elem):

    def attr_num(attrs: Dict[str, Any], k: str, min_value: int, max_value: int):
        val = elem.get(k, attrs.get(k, ""))
        if val == "":
            return
        if val == "max":
            val = max_value
        if val == "min":
            val = min_value
        val = np.clip(int(val), min_value, max_value)
        if "prefix" not in attrs or attrs["prefix"] == None:
            attrs["prefix"] = ""
        attrs["prefix"] += " " + f"[{k}_{val}]"

    attr_num(attrs, "oral", 0, 9)
    attr_num(attrs, "speed", 0, 9)
    attr_num(attrs, "laugh", 0, 2)
    attr_num(attrs, "break", 0, 7)


def apply_random_seed(attrs: dict):
    seed = attrs.get("seed", "")
    if seed == "random" or seed == "rand":
        seed = random.randint(0, 2**32 - 1)
        attrs["seed"] = seed
        logger.info(f"random seed: {seed}")


class NotSupportSSML(Exception):
    pass


def parse_ssml(ssml: str) -> List[Dict[str, Any]]:
    root = etree.fromstring(ssml)

    ssml_version = root.get("version", "NONE")
    if ssml_version != "0.1":
        raise NotSupportSSML("Unsupported ssml version: {ssml_version}")

    segments = []

    for voice in root.findall(".//voice"):
        voice_attrs = {
            "spk": voice.get("spk"),
            "style": voice.get("style"),
            "seed": voice.get("seed"),
            "top_p": voice.get("top_p"),
            "top_k": voice.get("top_k"),
            "temp": voice.get("temp"),
            "prompt1": voice.get("prompt1"),
            "prompt2": voice.get("prompt2"),
            "prefix": voice.get("prefix"),
            "normalize": voice.get("normalize"),
        }

        voice_attrs = {k: v for k, v in voice_attrs.items() if v is not None}

        expand_spk(voice_attrs)
        expand_style(voice_attrs)

        merge_prompt(voice_attrs, voice)
        apply_random_seed(voice_attrs)

        voice_segments = []

        if voice_attrs.get("temp", "") == "min":
            # ref: https://github.com/2noise/ChatTTS/issues/123#issue-2326908144
            voice_attrs["temp"] = 0.000000000001
        if voice_attrs.get("temp", "") == "max":
            voice_attrs["temp"] = 1

        # 处理 voice 开头的文本
        if voice.text and voice.text.strip():
            voice_segments.append(
                {"text": voice.text.strip(), "attrs": voice_attrs.copy()}
            )

        # 处理 voice 内部的文本和 prosody 元素
        for node in voice.iterchildren():
            if node.tag == "prosody":
                prosody_attrs = voice_attrs.copy()
                new_attrs = {
                    "rate": node.get("rate"),
                    "volume": node.get("volume"),
                    "pitch": node.get("pitch"),
                }
                prosody_attrs.update(
                    {k: v for k, v in new_attrs.items() if v is not None}
                )
                expand_style(prosody_attrs)
                merge_prompt(prosody_attrs, node)
                apply_random_seed(voice_attrs)

                if node.text and node.text.strip():
                    voice_segments.append(
                        {"text": node.text.strip(), "attrs": prosody_attrs}
                    )
            elif node.tag == "break":
                time_ms = int(node.get("time", "0").replace("ms", ""))
                segment = {"break": time_ms}
                voice_segments.append(segment)

            if node.tail and node.tail.strip():
                voice_segments.append(
                    {"text": node.tail.strip(), "attrs": voice_attrs.copy()}
                )

        end_segment = voice_segments[-1]
        end_segment["is_end"] = True

        segments = segments + voice_segments

    logger.info(f"collect len(segments): {len(segments)}")
    # logger.info(f"segments: {json.dumps(segments, ensure_ascii=False)}")

    return segments


if __name__ == "__main__":
    # 示例 SSML 输入
    ssml1 = """
    <speak version="0.1">
        <voice spk="20398768" seed="42" temp="min" top_p="0.9" top_k="20">
            电影中梁朝伟扮演的陈永仁的
            <prosody volume="5">
                编号27149
            </prosody>
            <prosody rate="2">
                编号27149
            </prosody>
            <prosody pitch="-12">
                编号27149
            </prosody>
            <prosody pitch="12">
                编号27149
            </prosody>
        </voice>
        <voice spk="20398768" seed="42" speed="9">
            编号27149
        </voice>
        <voice spk="20398768" seed="42">
            电影中梁朝伟扮演的陈永仁的编号27149
        </voice>
    </speak>
    """

    ssml2 = """
    <speak version="0.1">
        <voice spk="Bob">
            也可以合成多角色多情感的有声 [uv_break] 书 [uv_break] ,例如:
        </voice>
        <voice spk="Bob">
            黛玉冷笑道:
        </voice>
        <voice spk="female2">
            我说呢,亏了绊住,不然,早就飞了来了。
        </voice>
        <voice spk="Bob" speed="0">
            宝玉道:
        </voice>
        <voice spk="Alice">
            “只许和你玩,替你解闷。不过偶然到他那里,就说这些闲话。”
        </voice>
        <voice spk="female2">
            “好没意思的话!去不去,关我什么事儿?又没叫你替我解闷儿,还许你不理我呢”
        </voice>
        <voice spk="Bob">
            说着,便赌气回房去了。
        </voice>
    </speak>
    """
    ssml22 = """
<speak version="0.1">
    <voice spk="Bob" style="narration-relaxed">
        下面是一个 ChatTTS 用于合成多角色多情感的有声书示例
    </voice>
    <voice spk="Bob" style="narration-relaxed">
        黛玉冷笑道:
    </voice>
    <voice spk="female2" style="angry">
        我说呢 [uv_break] ,亏了绊住,不然,早就飞起来了。
    </voice>
    <voice spk="Bob" style="narration-relaxed">
        宝玉道:
    </voice>
    <voice spk="Alice" style="unfriendly">
        “只许和你玩 [uv_break] ,替你解闷。不过偶然到他那里,就说这些闲话。”
    </voice>
    <voice spk="female2" style="angry">
        “好没意思的话![uv_break] 去不去,关我什么事儿? 又没叫你替我解闷儿 [uv_break],还许你不理我呢”
    </voice>
    <voice spk="Bob" style="narration-relaxed">
        说着,便赌气回房去了。
    </voice>
</speak>
    """

    ssml3 = """
    <speak version="0.1">
        <voice spk="Bob" style="angry">
            “你到底在想什么?这已经是第三次了!每次我都告诉你要按时完成任务,可你总是拖延。你知道这对整个团队有多大的影响吗?!”
        </voice>
        <voice spk="Bob" style="assistant">
            “你到底在想什么?这已经是第三次了!每次我都告诉你要按时完成任务,可你总是拖延。你知道这对整个团队有多大的影响吗?!”
        </voice>
        <voice spk="Bob" style="gentle">
            “你到底在想什么?这已经是第三次了!每次我都告诉你要按时完成任务,可你总是拖延。你知道这对整个团队有多大的影响吗?!”
        </voice>
    </speak>
    """

    ssml4 = """
    <speak version="0.1">
        <voice spk="Bob" style="narration-relaxed">
            使用 prosody 控制生成文本的语速语调和音量,示例如下

            <prosody>
                无任何限制将会继承父级voice配置进行生成
            </prosody>
            <prosody rate="1.5">
                设置 rate 大于1表示加速,小于1为减速
            </prosody>
            <prosody pitch="6">
                设置 pitch 调整音调,设置为6表示提高6个半音
            </prosody>
            <prosody volume="2">
                设置 volume 调整音量,设置为2表示提高2个分贝
            </prosody>

            在 voice 中无prosody包裹的文本即为默认生成状态下的语音
        </voice>
    </speak>
    """

    ssml5 = """
    <speak version="0.1">
        <voice spk="Bob" style="narration-relaxed">
            使用 break 标签将会简单的
            
            <break time="500" />

            插入一段空白到生成结果中 
        </voice>
    </speak>
    """

    ssml6 = """
    <speak version="0.1">
        <voice spk="Bob" style="excited">
            temperature for sampling (may be overridden by style or speaker)
            <break time="500" />
            温度值用于采样,这个值有可能被 style 或者 speaker 覆盖 
            <break time="500" />
            temperature for sampling ,这个值有可能被 style 或者 speaker 覆盖 
            <break time="500" />
            温度值用于采样,(may be overridden by style or speaker)
        </voice>
    </speak>
    """

    segments = parse_ssml(ssml6)

    print(segments)

    # audio_segments = synthesize_segments(segments)
    # combined_audio = combine_audio_segments(audio_segments)

    # combined_audio.export("output.wav", format="wav")