import os
import re
import sys
import traceback
from io import BytesIO

import requests
from dotenv import load_dotenv


SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
load_dotenv(os.path.join(SCRIPT_DIR, ".env"))

# Eski Dyson demosundaki ses kimliğini koru; VOICE_DYSON tanımlıysa onu kullan.
DEFAULT_DYSON_VOICE_ID = "IuRRIAcbQK5AQk1XevPj"

try:
    sys.stdout.reconfigure(encoding="utf-8")
except Exception:
    pass


def number_to_turkish(number: int) -> str:
    """0-999999 arasındaki tam sayıları Türkçe seslendirme metnine çevirir."""
    ones = ["sıfır", "bir", "iki", "üç", "dört", "beş", "altı", "yedi", "sekiz", "dokuz"]
    tens = ["", "on", "yirmi", "otuz", "kırk", "elli", "altmış", "yetmiş", "seksen", "doksan"]

    if number < 0:
        return "eksi " + number_to_turkish(abs(number))
    if number < 10:
        return ones[number]
    if number < 100:
        return tens[number // 10] + (f" {ones[number % 10]}" if number % 10 else "")
    if number < 1000:
        hundreds = "yüz" if number // 100 == 1 else f"{ones[number // 100]} yüz"
        remainder = number % 100
        return hundreds + (f" {number_to_turkish(remainder)}" if remainder else "")
    if number < 1_000_000:
        thousands = "bin" if number // 1000 == 1 else f"{number_to_turkish(number // 1000)} bin"
        remainder = number % 1000
        return thousands + (f" {number_to_turkish(remainder)}" if remainder else "")
    return str(number)


def parse_turkish_integer(raw_number: str):
    """Tam sayıyı veya 3.000 / 16.999 biçimindeki binlik ayraçlı sayıyı ayrıştırır."""
    if re.fullmatch(r"\d{1,3}(?:\.\d{3})+", raw_number):
        return int(raw_number.replace(".", ""))
    if raw_number.isdigit():
        return int(raw_number)
    return None


def normalize_sentence(sentence: str) -> str:
    # Ürün metninde PM 2.5 varsa seslendirmeyi doğal tut.
    sentence = re.sub(r"\bPM\s*2[\.,]5\b", "PM iki buçuk", sentence, flags=re.IGNORECASE)

    # TL fiyatlarında noktalar binlik ayıracıdır: 3.000 TL -> üç bin lira.
    currency_pattern = re.compile(
        r"(?<!\w)(\d{1,3}(?:\.\d{3})+|\d+)\s*TL(?:['’](ye|ya))?",
        flags=re.IGNORECASE,
    )

    def currency_to_words(match: re.Match) -> str:
        amount = parse_turkish_integer(match.group(1))
        if amount is None:
            return match.group(0)
        suffix = "ya" if match.group(2) else ""
        return f"{number_to_turkish(amount)} lira{suffix}"

    sentence = currency_pattern.sub(currency_to_words, sentence)

    # Sayı metinde tek başına kaldıysa da TTS için yazıya çevir.
    number_pattern = re.compile(r"(?<![\w/])(?:\d{1,3}(?:\.\d{3})+|\d+)(?![\w/])")

    def number_to_words(match: re.Match) -> str:
        amount = parse_turkish_integer(match.group(0))
        return number_to_turkish(amount) if amount is not None else match.group(0)

    sentence = number_pattern.sub(number_to_words, sentence)
    sentence = sentence.replace("’", "'")
    sentence = re.sub(r"\s+", " ", sentence).strip()
    return sentence


def create_voice(text: str) -> BytesIO:
    api_key = os.getenv("ELEVENLABS_API_KEY")
    voice_id = (
        os.getenv("VOICE_DYSON")
        or os.getenv("VOICE_ID3")
        or os.getenv("VOICE_ID")
        or DEFAULT_DYSON_VOICE_ID
    )

    if not api_key:
        raise RuntimeError("ELEVENLABS_API_KEY tanımlı değil.")
    if not voice_id:
        raise RuntimeError("VOICE_DYSON, VOICE_ID3 veya VOICE_ID tanımlı değil.")

    response = requests.post(
        f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}",
        headers={
            "xi-api-key": api_key,
            "Content-Type": "application/json",
        },
        json={
            "text": text,
            "model_id": "eleven_v3",
            "languages": [{"language_id": "tr", "name": "Turkish"}],
            "voice_settings": {
                "stability": 0.72,
                "similarity_boost": 0.78,
                "style": 0.30,
                "speaker_boost": True,
                "speed": 0.96,
            },
            "pronunciation_guide": {
                "style": "sıcak, güven veren ve bilgilendirici radyo sunucusu",
                "hints": (
                    "Daysın HushJet adını net ve doğal telaffuz et. "
                    "Hava kalitesi bilgisini sakin ve tarafsız aktar. "
                    "İndirim ve fiyat bilgisini anlaşılır oku. "
                    "Web sitesi ve mağaza çağrısını doğal bir tonla aktar. "
                    "Son marka imzasını güven veren bir tonla bitir. "
                    "Metne yeni kelime, açıklama veya sağlık vaadi ekleme."
                ),
            },
        },
        timeout=60,
    )

    if response.status_code >= 400:
        raise RuntimeError(f"ElevenLabs HTTP {response.status_code}: {response.text[:500]}")

    return BytesIO(response.content)


def main() -> None:
    log_file = os.path.join(SCRIPT_DIR, "dyson_hava_py.log")

    try:
        if len(sys.argv) < 3:
            raise ValueError("Kullanım: python dyson-hava.py <sentence> <output_path>")

        sentence = normalize_sentence(sys.argv[1])
        output_path = sys.argv[2]

        print(f"Dyson HushJet metni: {sentence}")
        print(f"Çıkış: {output_path}")

        audio_stream = create_voice(sentence)
        output_dir = os.path.dirname(os.path.abspath(output_path))
        os.makedirs(output_dir, exist_ok=True)

        with open(output_path, "wb") as output_file:
            output_file.write(audio_stream.read())

        print(f"OK: Dyson HushJet sesi yazıldı -> {output_path}")

    except Exception as error:
        error_text = f"ERROR (dyson-hava): {error}\n{traceback.format_exc()}"
        try:
            with open(log_file, "a", encoding="utf-8") as log:
                log.write("\n" + ("-" * 60) + "\n")
                log.write(error_text + "\n")
        except Exception:
            pass

        print(error_text)
        raise SystemExit(1)


if __name__ == "__main__":
    main()
