import os
import sys
import re
import requests

missing = []
for lib in ["requests"]:
    try:
        __import__(lib)
    except ImportError:
        missing.append(lib)

if missing:
    print(f"Error: Missing libraries:\n{', '.join(missing)}")
    print("  install: pip install requests")
    input("\nPress ENTER to terminate>>\n")
    sys.exit(1)

from tktts_base import apply_replacements, normalize_speaker, split_dialogue


# VOICEVOXエンジンの設定
TTS_ENGINE_NAME = 'voicevox'
DEFAULT_VOICEVOX_VOICE = "四国めたん"
DEFAULT_ENDPOINT = "http://127.0.0.1:50021"  # エンジン起動時のデフォルトURL


def get_available_voices_info(endpoint=DEFAULT_ENDPOINT):
    response = requests.get(f"{endpoint}/speakers")
    response.raise_for_status()
    speakers = response.json()
    speakers_dict = []

    voices = []
    for sp in speakers:
        name = sp["name"]
        for style in sp["styles"]:
#            print(f"{name} - {style['name']} (id={style['id']})")
#            speakers_dict.append(sp)
            voices.append({"name": name, "style": style['name'], "id": style['id']})

    return voices

def get_available_voices(endpoint=DEFAULT_ENDPOINT):
    voices = get_available_voices_info(endpoint)
    if not voices: return False

    voice_names = []
    for v in voices:
        voice_names.append(f"{v['name']}（{v['style']}）")

    return voice_names

def list_available_voices(endpoint=DEFAULT_ENDPOINT):
    print(f"=== 利用可能な {TTS_ENGINE_NAME} voices ===")
    voices = get_available_voices_info()
    if not voices: return False
    
    for v in voices:
        print(f"  Name: {v['name']}, Style: {v['style']}, ID: {v['id']}")

    return True

def resolve_speaker_id(speaker_name, endpoint=DEFAULT_ENDPOINT, speakers_dict = None):
    """
    speaker_name にフルネームや部分文字列を渡すと、
    最初に一致した speaker_id を返す。
    """

    if speakers_dict is None: speakers_dict = get_available_voices_info(endpoint)
    speaker_name = normalize_speaker(speaker_name) #.lower())
#    print(f"  search for [{speaker_name}]")
#    print(f"  speakers_dict:", speakers_dict)
    for voice in speakers_dict:
        full_name = voice['name'].lower()
        style = voice['style'].lower()
        if speaker_name in full_name or speaker_name in style:
            return speakers_dict, voice["id"]

    raise ValueError(f"❌ Error in tktts_voicevox.resolve_speaker_id(): 話者 [{speaker_name}] が見つかりませんでした")

def speak(outfile, text, voice, speak_rate = None, speak_pitch = None, endpoint = DEFAULT_ENDPOINT):
    query = requests.post(
        f"{endpoint}/audio_query",
        params={"text": text, "speaker": voice}
        )
    query.raise_for_status()
    query_json = query.json()

    if speak_rate is not None:
        query_json["speedScale"] = speak_rate
    if speak_pitch is not None:
        query_json["pitchScale"] = speak_pitch

    try:
        synthesis = requests.post(
            f"{endpoint}/synthesis",
            params={"speaker": voice},
            json=query_json
            )
        synthesis.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"❌ VOICEVOX 接続エラー: {e}")
        return None

    with open(outfile, "wb") as f:
        f.write(synthesis.content)

    if os.path.exists(outfile):
        print(f"    ** 一時ファイル [{outfile}] を保存しました")
    else:
        print(f"    ** Error: ファイル [{outfile}] の出力に失敗しました")
        return None

    return outfile

# target_voicesが文字列型の場合、speakerを強制的に置き換える
def speak_dialogue(dialogue, replacements, target_voices, speakers = {}, temp_dir=".",
                   outfile=None, ext="wav", endpoint=DEFAULT_ENDPOINT, 
                   cfg = None):
    is_save_mode = bool(outfile)

#    print()
    print("tktts_voicevox.speak_dialogue(): target_voices:", target_voices)

    tmpfiles = []
    speakers_dict = None   # 同じspekaerを何度も検索しなくて済むようにglobalな辞書を作る
    idx = 1
    for i, _dialogue in enumerate(dialogue):
        print()
        print(f"Dialogue {i:04d}:")
        dialogue_list = split_dialogue(_dialogue, target_voices, speakers = speakers, 
                            default_voice = DEFAULT_VOICEVOX_VOICE, is_monologue = cfg.monologue)
        for speaker, text in dialogue_list:
            tmpfile = os.path.join(temp_dir, f"tmp_{idx:03d}.{ext}")
            text = apply_replacements(text, replacements)
            if type(target_voices) is str: speaker = target_voices

            try:
                speakers_dict, target_voice = resolve_speaker_id(speaker, endpoint, speakers_dict = speakers_dict)
            except ValueError as e:
                print(e)
                return False, tmpfiles

            print(f"  {idx:04d}: voice={speaker} (id={target_voice}): ", end = "")
            print(text)

            speedScale = cfg.fspeak_rate  if hasattr(cfg, 'fspeak_rate') else None
            pitchScale = cfg.fspeak_pitch if hasattr(cfg, 'fspeak_pitch') else None
            _outfile = speak(outfile = tmpfile,text = text, voice = target_voice,
                        speak_rate = speedScale, speak_pitch = pitchScale,
                        endpoint = endpoint)
            
            if _outfile is None:
                return False, tmpfiles

            tmpfiles.append(tmpfile)
            idx += 1

    return True, tmpfiles

