'''
启用tecent翻译 可以在YAML 中填入下面的参数
ng_voice_translate_on : True
tencentcloud_common_region : "ap-shanghai"
tencentcloud_common_secretid : "xxxxx"
tencentcloud_common_secretkey : "xxxxx"
ng_voice_tar : 'ja'
'''

from .Extension import Extension
import urllib
import requests
import uuid
import os
import base64
from aiohttp import request
from binascii import b2a_base64
from hashlib import sha1
from urllib.parse import urlencode
from hmac import new
import random
from sys import maxsize, version_info
from time import time
from nonebot import get_driver
from aiohttp import request
from loguru import logger
from nonebot.exception import ActionFailed
import asyncio


try:
    from ujson import loads as loadJsonS
except:
    from json import loads as loadJsonS


# 扩展的配置信息，用于ai理解扩展的功能 *必填*
ext_config: dict = {
    "name": "voice",   # 扩展名称，用于标识扩展
    "arguments": {
        'sentence': 'str',  # 需要转换的文本
        'emotion': 'str',   # 情感
    },
    # 扩展的描述信息，用于提示ai理解扩展的功能 *必填* 尽量简短 使用英文更节省token
    "description": "Send a voice sentence. The emotional parameter must be one of \"normal,sweet,tsundere,sexy,whisper,murmur\" (usage in response: /#voice&hello&sweet#/) ",
    # 参考词，用于上下文参考使用，为空则每次都会被参考(消耗token)
    "refer_word": [],
    # 作者信息
    "author": "恋如雨止",
    # 版本
    "version": "0.0.2",
    # 扩展简介
    "intro": "发送语音消息(支持翻译)",
}

# 情感参数表
emotion_rate_dict = {
    'normal': {
        'custom_attributes': {
            "speed_scale": 1,
            "volume_scale": 1,
            "intonation_scale": 1,
            "pre_phoneme_length": 0.1,
            "post_phoneme_length": 0.1,
        },
        'name': 'ノーマル',
    },
    'sweet': {
        'custom_attributes': {
            "speed_scale": 1.1,
            "volume_scale": 0.9,
            "intonation_scale": 1.3,
            "pre_phoneme_length": 0.2,
            "post_phoneme_length": 0.2,
        },
        'name': 'あまあま',
    },
    'tsundere': {
        'custom_attributes': {
            "speed_scale": 1.0,
            "volume_scale": 1.1,
            "intonation_scale": 1.2,
            "pre_phoneme_length": 0.3,
            "post_phoneme_length": 0.3,
        },
        'name': 'ツンツン',
    },
    'sexy': {
        'custom_attributes': {
            "speed_scale": 0.9,
            "volume_scale": 1.2,
            "intonation_scale": 1.1,
            "pre_phoneme_length": 0.4,
            "post_phoneme_length": 0.4,
        },
        'name': 'セクシー',
    },
    'whisper': {
        'custom_attributes': {
            "speed_scale": 0.8,
            "volume_scale": 1.3,
            "intonation_scale": 1.0,
            "pre_phoneme_length": 0.5,
            "post_phoneme_length": 0.5,
        },
        'name': 'ささやき',
    },
    'murmur': {
        'custom_attributes': {
            "speed_scale": 0.7,
            "volume_scale": 1.4,
            "intonation_scale": 0.9,
            "pre_phoneme_length": 0.6,
            "post_phoneme_length": 0.6,
        },
        'name': 'ヒソヒソ',
    },
}

# 情感翻译映射表
emotion_translate_jp2en = {
    'ノーマル': 'normal',
    'あまあま': 'sweet',
    'ツンツン': 'tsundere',
    'セクシー': 'sexy',
    'ささやき': 'whisper',
    'ヒソヒソ': 'murmur',
}
emotion_translate_en2jp = {f: t for t, f in emotion_translate_jp2en.items()}

class CustomExtension(Extension):
    async def call(self, arg_dict: dict, ctx_data: dict) -> dict:
        """ 当扩展被调用时执行的函数 *由扩展自行实现*

        参数:
            arg_dict: dict, 由ai解析的参数字典 {参数名: 参数值}
        """
        custom_config: dict = self.get_custom_config()  # 获取yaml中的配置信息

        ng_voice_translate_on = custom_config.get(
            'ng_voice_translate_on', False)    # 是否启用翻译
        tencentcloud_common_region = custom_config.get(
            'tencentcloud_common_region', "ap-shanghai")  # 腾讯翻译-地区
        tencentcloud_common_secretid = custom_config.get(
            'tencentcloud_common_secretid', "xxxxx")    # 腾讯翻译-密钥id
        tencentcloud_common_secretkey = custom_config.get(
            'tencentcloud_common_secretkey', "xxxxx")  # 腾讯翻译-密钥
        ng_voice_tar = custom_config.get('g_voice_tar', 'ja')   # 翻译目标语言
        is_base64 = custom_config.get('is_base64', False)   # 是否使用base64编码

        character = custom_config.get('character', 'もち子さん')  # 人物
        url = custom_config.get('api_url', '127.0.0.1:50021')

        if not url:    # 如果没有配置语音服务器url则返回错误信息
            return {'text': f"[ext_VOICEVOX] 未配置语音服务器url"}
        if not url.startswith('http'):   # 如果不是http开头则添加
            url = f'http://{url}'
        if not url.endswith('/'):   # 如果不是/结尾则添加
            url = f'{url}/'

        # 音频缓存文件夹
        voice_path = 'voice_cache/'
        if not os.path.exists(voice_path):
            os.mkdir(voice_path)

        # 获取参数
        raw_text = arg_dict.get('sentence', None)
        emotion_key = arg_dict.get('emotion', 'normal')
        # 判断情感索引是否存在 如果不存在则使用默认情感
        if emotion_key not in self.character_emotion_dict[character]:
            emotion_key = 'normal'

        """ 腾讯翻译 """
        # 腾讯翻译-签名
        config = get_driver().config

        async def getReqSign(params: dict) -> str:
            common = {
                "Action": "TextTranslate",
                "Region": f"{tencentcloud_common_region}",
                "Timestamp": int(time()),
                "Nonce": random.randint(1, maxsize),
                "SecretId": f"{tencentcloud_common_secretid}",
                "Version": "2018-03-21",
            }
            params.update(common)
            sign_str = "POSTtmt.tencentcloudapi.com/?"
            sign_str += "&".join("%s=%s" %
                                 (k, params[k]) for k in sorted(params))
            secret_key = tencentcloud_common_secretkey
            if version_info[0] > 2:
                sign_str = bytes(sign_str, "utf-8")
                secret_key = bytes(secret_key, "utf-8")
            hashed = new(secret_key, sign_str, sha1)
            signature = b2a_base64(hashed.digest())[:-1]
            if version_info[0] > 2:
                signature = signature.decode()
            return signature

        async def q_translate(message) -> str:
            _source_text = message
            _source = "auto"
            _target = ng_voice_tar
            try:
                endpoint = "https://tmt.tencentcloudapi.com"
                params = {
                    "Source": _source,
                    "SourceText": _source_text,
                    "Target": _target,
                    "ProjectId": 0,
                }
                params["Signature"] = await getReqSign(params)
                # 加上超时参数
                async with request("POST", endpoint, data=params) as resp:
                    data = loadJsonS(await asyncio.wait_for(resp.read(), timeout=30))["Response"]
                    message = data["TargetText"]
            except ActionFailed as e:
                logger.warning(
                    f"ActionFailed {e.info['retcode']} {e.info['msg'].lower()} {e.info['wording']}"
                )
            except TimeoutError as e:
                logger.warning(
                    f"TimeoutError {e}"
                )
            return message

        """ 腾讯翻译结束 """

        if ng_voice_translate_on == True:
            t_result = await q_translate(raw_text)
        else:
            t_result = raw_text
        text = t_result + '~'  # 加上一个字符，避免合成语音丢失结尾

        # 从self.character_emotion_dict中获取角色，如果emotion_key不存在则使用第一个
        speaker = self.character_emotion_dict[character][emotion_translate_en2jp[emotion_key]]['speaker'] if emotion_translate_en2jp[
            emotion_key] in self.character_emotion_dict[character] else self.character_emotion_dict[character][0]['speaker']
        # 根据emotion_key获取从emotion_rate_dict获取自定义属性
        custom_attributes = emotion_rate_dict[emotion_key]['custom_attributes']

        # 发送查询请求并保存结果
        params = {
            "text": text,
            "speaker": speaker,
        }
        params_encoded = urlencode(params)
        res = requests.post(url + "audio_query?" + params_encoded)
        query_json = res.json()

        # 更新voicevox_query属性
        query_json['speedScale'] = custom_attributes["speed_scale"]
        query_json['volumeScale'] = custom_attributes["volume_scale"]
        query_json['intonationScale'] = custom_attributes["intonation_scale"]
        query_json['prePhonemeLength'] = custom_attributes["pre_phoneme_length"]
        query_json['postPhonemeLength'] = custom_attributes["post_phoneme_length"]

        # 发送语音合成请求并保存结果
        synthesis_params = {
            "speaker": speaker
        }
        params_encoded = urlencode(synthesis_params)
        res = requests.post(f"{url}synthesis?{params_encoded}", json=query_json, timeout=120)
        audio_data = res.content

        file_name = f"{voice_path}{uuid.uuid1()}.wav"

        if is_base64:
            audio_data = base64.b64decode(audio_data)

        with open(file_name, "wb") as f:
            f.write(audio_data)

        local_url = f"file:///{os.path.abspath(file_name)}"

        if text is not None:
            return {
                'voice': local_url,             # 语音url
                'text': f"[语音] {raw_text}",    # 文本
            }
        return {}

    def __init__(self, custom_config: dict):
        super().__init__(ext_config.copy(), custom_config)

        url = custom_config.get('api_url', '127.0.0.1:50021')

        if not url:    # 如果没有配置语音服务器url则返回错误信息
            raise Exception("未配置语音服务器url")
        if not url.startswith('http'):   # 如果不是http开头则添加
            url = f'http://{url}'
        if not url.endswith('/'):   # 如果不是/结尾则添加
            url = f'{url}/'

        # 从api获取可用角色json
        for _ in range(3):
            try:
                res = requests.get(url + "speakers", timeout=10)
                break
            except requests.exceptions.RequestException as e:
                continue
        else:
            raise Exception("获取语音服务器角色列表失败")
        speaker_json = res.json()

        self.character_emotion_dict = {}

        # 遍历角色json，获取角色列表，保存到 character_emotion_dict 中
        for character in speaker_json:
            character_name = character["name"]
            styles = character["styles"]
            em_dict = {}
            for style in styles:
                em_dict[style["name"]] = {
                    "speaker": style["id"],
                    "name": style["name"],
                }
            self.character_emotion_dict[character_name] = em_dict

        print(f"[ext_VOICEVOX] 共加载了 {len(self.character_emotion_dict)} 个角色")
