"""
Twilio Client Module

Provides Twilio API wrapper and TwiML generation.
"""

import asyncio
from typing import Optional

from twilio.rest import Client

from ..config import (
    TWILIO_ACCOUNT_SID,
    TWILIO_AUTH_TOKEN,
    WEBSOCKET_URL,
    STREAM_REDIRECT_URL,
    validate_twilio_credentials,
    log_call,
)


class TwilioHelper:
    """
    Helper class for Twilio operations.

    Provides TwiML generation and call manipulation.
    """

    def __init__(
        self,
        account_sid: str = None,
        auth_token: str = None,
        websocket_url: str = WEBSOCKET_URL,
        stream_redirect_url: str = STREAM_REDIRECT_URL,
    ):
        """
        Initialize Twilio helper.

        Args:
            account_sid: Twilio account SID (uses env var if not provided)
            auth_token: Twilio auth token (uses env var if not provided)
            websocket_url: URL for media stream WebSocket
            stream_redirect_url: URL for stream redirect after TTS
        """
        self.account_sid = account_sid or TWILIO_ACCOUNT_SID
        self.auth_token = auth_token or TWILIO_AUTH_TOKEN
        self.websocket_url = websocket_url
        self.stream_redirect_url = stream_redirect_url
        self._client: Optional[Client] = None

    @property
    def client(self) -> Client:
        """Get Twilio client, initializing if needed"""
        if self._client is None:
            if not self.account_sid or not self.auth_token:
                raise RuntimeError(
                    "Twilio credentials not set. Please set TWILIO_ACCOUNT_SID and "
                    "TWILIO_AUTH_TOKEN environment variables."
                )
            self._client = Client(self.account_sid, self.auth_token)
        return self._client

    def build_greeting_twiml(self, custom_greeting: str = None) -> str:
        """
        Build TwiML for incoming call greeting.

        Args:
            custom_greeting: Optional custom greeting text

        Returns:
            TwiML XML string
        """
        greeting = custom_greeting or (
            '<Say voice="alice">Hello.</Say>\n'
            '  <Pause length="1"/>\n'
            '  <Say voice="alice">This is Ring A I.</Say>\n'
            '  <Pause length="1"/>\n'
            '  <Say voice="alice">Please speak clearly after the tone.</Say>'
        )

        return f'''<?xml version="1.0" encoding="UTF-8"?>
<Response>
  {greeting}
  <Connect>
    <Stream url="{self.websocket_url}"/>
  </Connect>
</Response>'''

    def build_say_twiml(self, text: str) -> str:
        """
        Build TwiML for AI response.

        Args:
            text: Text to speak

        Returns:
            TwiML XML string
        """
        return f'''<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice="alice">{text}</Say>
  <Pause length="1"/>
  <Redirect method="POST">{self.stream_redirect_url}</Redirect>
</Response>'''

    def build_stream_twiml(self) -> str:
        """
        Build TwiML for resuming stream after TTS.

        Returns:
            TwiML XML string
        """
        return f'''<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Connect>
    <Stream url="{self.websocket_url}"/>
  </Connect>
</Response>'''

    def build_hangup_twiml(self) -> str:
        """
        Build TwiML for hanging up.

        Returns:
            TwiML XML string
        """
        return '''<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Hangup/>
</Response>'''

    async def say(self, call_id: str, text: str):
        """
        Send TTS to active call.

        Args:
            call_id: Twilio call SID
            text: Text to speak
        """
        log_call.info("call=%s TTS -> %r", call_id, text)

        twiml = self.build_say_twiml(text)

        try:
            await asyncio.to_thread(
                self.client.calls(call_id).update,
                twiml=twiml
            )
        except Exception as e:
            # Silently handle "call not in progress" errors
            if "21220" in str(e) or "not in-progress" in str(e).lower():
                log_call.info("call=%s already ended, cannot send TTS", call_id)
            else:
                raise

    def get_call(self, call_id: str):
        """Get call resource by SID"""
        return self.client.calls(call_id).fetch()


# Global Twilio helper instance
_twilio_helper: Optional[TwilioHelper] = None


def get_twilio_client() -> TwilioHelper:
    """Get global Twilio helper instance"""
    global _twilio_helper
    if _twilio_helper is None:
        _twilio_helper = TwilioHelper()
    return _twilio_helper


async def trigger_say(call_id: str, text: str):
    """
    Convenience function to send TTS to call.

    Uses global Twilio helper instance.
    """
    helper = get_twilio_client()
    await helper.say(call_id, text)
