#!/usr/bin/env python3
"""
Text Normalization for ElevenLabs TTS

Preprocesses text to improve speech synthesis quality.
Handles currencies, phone numbers, dates, percentages, and common abbreviations.

Usage:
    python normalize_tts.py "Your text with $100 and 555-123-4567"
    
Or import as module:
    from normalize_tts import normalize_for_tts
    clean_text = normalize_for_tts("Your text here")

Requirements:
    pip install inflect
"""

import re
import sys
from typing import Optional

try:
    import inflect
    p = inflect.engine()
except ImportError:
    print("Error: inflect library required. Run: pip install inflect")
    sys.exit(1)


def normalize_for_tts(text: str) -> str:
    """
    Normalize text for text-to-speech processing.
    
    Converts:
    - Currencies ($, £, €, ¥) to spoken form
    - Phone numbers to digit-by-digit
    - Percentages to spoken form
    - Common abbreviations
    - Dates (basic patterns)
    - Times
    """
    
    # Currency conversion
    def money_replacer(match):
        currency_map = {
            "$": ("dollars", "cents"),
            "£": ("pounds", "pence"),
            "€": ("euros", "cents"),
            "¥": ("yen", "sen"),
            "₹": ("rupees", "paise"),
        }
        symbol, num = match.groups()
        num_clean = num.replace(',', '')
        
        main_unit, sub_unit = currency_map.get(symbol, ("currency units", "subunits"))
        
        if '.' in num_clean:
            main, sub = num_clean.split('.')
            main_words = p.number_to_words(int(main))
            sub_words = p.number_to_words(int(sub))
            return f"{main_words} {main_unit} and {sub_words} {sub_unit}"
        
        return f"{p.number_to_words(int(num_clean))} {main_unit}"
    
    text = re.sub(r'([$£€¥₹])(\d+(?:,\d{3})*(?:\.\d{1,2})?)', money_replacer, text)
    
    # US phone numbers
    def phone_replacer(match):
        groups = match.groups()
        return ", ".join(
            " ".join(p.number_to_words(int(d)) for d in g) 
            for g in groups
        )
    
    text = re.sub(r'(\d{3})-(\d{3})-(\d{4})', phone_replacer, text)
    text = re.sub(r'\((\d{3})\)\s*(\d{3})-(\d{4})', phone_replacer, text)
    
    # Percentages
    def percent_replacer(match):
        num = match.group(1).replace(',', '')
        if '.' in num:
            return f"{p.number_to_words(float(num))} percent"
        return f"{p.number_to_words(int(num))} percent"
    
    text = re.sub(r'(\d+(?:,\d{3})*(?:\.\d+)?)\s*%', percent_replacer, text)
    
    # Times (12-hour)
    def time_12_replacer(match):
        hour, minute, period = match.groups()
        hour_int = int(hour)
        minute_int = int(minute)
        
        if minute_int == 0:
            return f"{p.number_to_words(hour_int)} {period}"
        elif minute_int < 10:
            return f"{p.number_to_words(hour_int)} oh {p.number_to_words(minute_int)} {period}"
        else:
            return f"{p.number_to_words(hour_int)} {p.number_to_words(minute_int)} {period}"
    
    text = re.sub(r'(\d{1,2}):(\d{2})\s*(AM|PM|am|pm)', time_12_replacer, text)
    
    # Times (24-hour)
    def time_24_replacer(match):
        hour, minute = match.groups()
        hour_int = int(hour)
        minute_int = int(minute)
        
        # Convert to 12-hour for natural speech
        period = "AM" if hour_int < 12 else "PM"
        if hour_int == 0:
            hour_int = 12
        elif hour_int > 12:
            hour_int -= 12
        
        if minute_int == 0:
            return f"{p.number_to_words(hour_int)} {period}"
        elif minute_int < 10:
            return f"{p.number_to_words(hour_int)} oh {p.number_to_words(minute_int)} {period}"
        else:
            return f"{p.number_to_words(hour_int)} {p.number_to_words(minute_int)} {period}"
    
    text = re.sub(r'(\d{1,2}):(\d{2})(?!\s*(?:AM|PM|am|pm))', time_24_replacer, text)
    
    # Common abbreviations
    abbreviations = {
        r'\bDr\.\s': 'Doctor ',
        r'\bMr\.\s': 'Mister ',
        r'\bMrs\.\s': 'Missus ',
        r'\bMs\.\s': 'Miss ',
        r'\bProf\.\s': 'Professor ',
        r'\bSr\.\s': 'Senior ',
        r'\bJr\.\s': 'Junior ',
        r'\bSt\.\s(?!Patrick|Peter|Paul|Mary|John|George|Andrew|David)': 'Street ',
        r'\bAve\.\s': 'Avenue ',
        r'\bBlvd\.\s': 'Boulevard ',
        r'\bRd\.\s': 'Road ',
        r'\bLn\.\s': 'Lane ',
        r'\bCt\.\s': 'Court ',
        r'\bApt\.\s': 'Apartment ',
        r'\betc\.\s': 'etcetera ',
        r'\be\.g\.\s': 'for example ',
        r'\bi\.e\.\s': 'that is ',
        r'\bvs\.\s': 'versus ',
        r'\bNo\.\s': 'Number ',
    }
    
    for pattern, replacement in abbreviations.items():
        text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
    
    # Technical abbreviations (expand fully)
    tech_abbreviations = {
        r'\bAPI\b': 'A P I',
        r'\bURL\b': 'U R L',
        r'\bHTML\b': 'H T M L',
        r'\bCSS\b': 'C S S',
        r'\bPHP\b': 'P H P',
        r'\bSQL\b': 'sequel',
        r'\bMySQL\b': 'my sequel',
        r'\bCDN\b': 'C D N',
        r'\bDNS\b': 'D N S',
        r'\bVPS\b': 'V P S',
        r'\bSEO\b': 'S E O',
        r'\bCTA\b': 'C T A',
        r'\bROI\b': 'R O I',
        r'\bKPI\b': 'K P I',
        r'\bSaaS\b': 'sass',
        r'\bTTFB\b': 'time to first byte',
    }
    
    for pattern, replacement in tech_abbreviations.items():
        text = re.sub(pattern, replacement, text)
    
    # Units
    units = {
        r'(\d+)\s*KB\b': lambda m: f"{p.number_to_words(int(m.group(1)))} kilobytes",
        r'(\d+)\s*MB\b': lambda m: f"{p.number_to_words(int(m.group(1)))} megabytes",
        r'(\d+)\s*GB\b': lambda m: f"{p.number_to_words(int(m.group(1)))} gigabytes",
        r'(\d+)\s*TB\b': lambda m: f"{p.number_to_words(int(m.group(1)))} terabytes",
        r'(\d+)\s*ms\b': lambda m: f"{p.number_to_words(int(m.group(1)))} milliseconds",
        r'(\d+)\s*sec\b': lambda m: f"{p.number_to_words(int(m.group(1)))} seconds",
        r'(\d+)\s*min\b': lambda m: f"{p.number_to_words(int(m.group(1)))} minutes",
        r'(\d+)\s*hr\b': lambda m: f"{p.number_to_words(int(m.group(1)))} hours",
        r'(\d+)\s*km\b': lambda m: f"{p.number_to_words(int(m.group(1)))} kilometers",
        r'(\d+)\s*m\b(?!onth|in)': lambda m: f"{p.number_to_words(int(m.group(1)))} meters",
    }
    
    for pattern, replacer in units.items():
        text = re.sub(pattern, replacer, text, flags=re.IGNORECASE)
    
    # Ordinals at start of sentences or after common patterns
    def ordinal_replacer(match):
        num = match.group(1)
        return p.ordinal(int(num))
    
    text = re.sub(r'\b(\d+)(?:st|nd|rd|th)\b', ordinal_replacer, text)
    
    # Clean up multiple spaces
    text = re.sub(r'\s+', ' ', text)
    
    return text.strip()


def add_audio_tags(text: str, style: str = "conversational") -> str:
    """
    Add basic audio tags for Eleven v3.
    
    Styles:
    - conversational: Natural pauses, thoughtful delivery
    - professional: Clear, measured, authoritative
    - enthusiastic: Energetic, excited delivery
    - tutorial: Patient, clear explanations
    """
    
    style_prefix = {
        "conversational": "[conversational] ",
        "professional": "[professional] ",
        "enthusiastic": "[excited] ",
        "tutorial": "[clear, patient] ",
    }
    
    prefix = style_prefix.get(style, "")
    
    # Add pause hints at natural break points
    text = re.sub(r'\.{3}', ' [short pause] ', text)  # Ellipses
    text = re.sub(r'\s+—\s+', ' [short pause] ', text)  # Em dashes
    
    return prefix + text


def process_for_blog_audio(text: str) -> str:
    """
    Process blog post text for audio conversion.
    Removes markdown, normalizes text, adds natural pauses.
    """
    
    # Remove markdown formatting
    text = re.sub(r'\*\*(.+?)\*\*', r'\1', text)  # Bold
    text = re.sub(r'\*(.+?)\*', r'\1', text)  # Italic
    text = re.sub(r'`(.+?)`', r'\1', text)  # Inline code
    text = re.sub(r'\[(.+?)\]\(.+?\)', r'\1', text)  # Links
    text = re.sub(r'^#+\s*', '', text, flags=re.MULTILINE)  # Headers
    text = re.sub(r'^[-*]\s*', '', text, flags=re.MULTILINE)  # List items
    text = re.sub(r'^\d+\.\s*', '', text, flags=re.MULTILINE)  # Numbered lists
    
    # Remove code blocks
    text = re.sub(r'```[\s\S]*?```', '[code example omitted]', text)
    
    # Normalize
    text = normalize_for_tts(text)
    
    # Add style
    text = add_audio_tags(text, "conversational")
    
    return text


if __name__ == "__main__":
    if len(sys.argv) > 1:
        input_text = " ".join(sys.argv[1:])
        print("\nOriginal:")
        print(input_text)
        print("\nNormalized:")
        print(normalize_for_tts(input_text))
    else:
        # Demo
        examples = [
            "$1,234.56",
            "Call me at 555-123-4567",
            "The meeting is at 14:30",
            "Speed improved by 45%",
            "File size: 250MB",
            "Dr. Smith lives on Oak St. near Ave. B",
            "Check the API documentation for URL formats",
            "Response time dropped to 200ms",
            "This is the 3rd time I've mentioned this",
        ]
        
        print("Text Normalization Examples")
        print("=" * 50)
        
        for example in examples:
            print(f"\nInput:  {example}")
            print(f"Output: {normalize_for_tts(example)}")
